refactor(dart/transform): Correct Dart analyzer warnings

- Fix numerous Dart analyzer warnings we had been ignoring.
- Delete unused `in_progress` dir
This commit is contained in:
Tim Blasi 2015-04-13 16:54:09 -07:00
parent aba61f22a6
commit 7cac7c5157
28 changed files with 123 additions and 1254 deletions

View File

@ -14,6 +14,7 @@ dependencies:
code_transformers: '^0.2.5' code_transformers: '^0.2.5'
dart_style: '^0.1.3' dart_style: '^0.1.3'
html: '^0.12.0' html: '^0.12.0'
source_span: '^1.0.0'
stack_trace: '^1.1.1' stack_trace: '^1.1.1'
transformers: transformers:
- angular2 - angular2

View File

@ -16,6 +16,36 @@ class Html5LibDomAdapter implements DomAdapter {
'tabindex': 'tabIndex', 'tabindex': 'tabIndex',
}; };
@override
getGlobalEventTarget(String target) {
throw 'not implemented';
}
@override
getTitle() {
throw 'not implemented';
}
@override
setTitle(String newTitle) {
throw 'not implemented';
}
@override
String getEventKey(event) {
throw 'not implemented';
}
@override
void replaceChild(el, newNode, oldNode) {
throw 'not implemented';
}
@override
dynamic getBoundingClientRect(el) {
throw 'not implemented';
}
Element parse(String templateHtml) => parser.parse(templateHtml).firstChild; Element parse(String templateHtml) => parser.parse(templateHtml).firstChild;
query(selector) { query(selector) {
throw 'not implemented'; throw 'not implemented';
@ -54,7 +84,7 @@ class Html5LibDomAdapter implements DomAdapter {
String type(node) { String type(node) {
throw 'not implemented'; throw 'not implemented';
} }
content(TemplateElement node) { content(node) {
throw 'not implemented'; throw 'not implemented';
} }

View File

@ -1,7 +1,9 @@
library angular2.transform.common.logging; library angular2.transform.common.logging;
import 'dart:async';
import 'package:barback/barback.dart'; import 'package:barback/barback.dart';
import 'package:code_transformers/messages/build_logger.dart'; import 'package:code_transformers/messages/build_logger.dart';
import 'package:source_span/source_span.dart';
BuildLogger _logger; BuildLogger _logger;
@ -24,6 +26,11 @@ BuildLogger get logger {
} }
class PrintLogger implements BuildLogger { class PrintLogger implements BuildLogger {
@override
final String detailsUri = '';
@override
final bool convertErrorsToWarnings = false;
void _printWithPrefix(prefix, msg) => print('$prefix: $msg'); void _printWithPrefix(prefix, msg) => print('$prefix: $msg');
void info(msg, {AssetId asset, SourceSpan span}) => void info(msg, {AssetId asset, SourceSpan span}) =>
_printWithPrefix('INFO', msg); _printWithPrefix('INFO', msg);
@ -43,7 +50,7 @@ class PrintLoggerError extends Error {
final AssetId asset; final AssetId asset;
final SourceSpan span; final SourceSpan span;
PrintLoggerError(message, asset, span); PrintLoggerError(this.message, this.asset, this.span);
@override @override
String toString() { String toString() {

View File

@ -3,6 +3,7 @@ library angular2.transform.common.names;
const SETUP_METHOD_NAME = 'initReflector'; const SETUP_METHOD_NAME = 'initReflector';
const REFLECTOR_VAR_NAME = 'reflector'; const REFLECTOR_VAR_NAME = 'reflector';
const DEPS_EXTENSION = '.ng_deps.dart'; const DEPS_EXTENSION = '.ng_deps.dart';
const REFLECTION_CAPABILITIES_NAME = 'ReflectionCapabilities';
const REGISTER_TYPE_METHOD_NAME = 'registerType'; const REGISTER_TYPE_METHOD_NAME = 'registerType';
const REGISTER_GETTERS_METHOD_NAME = 'registerGetters'; const REGISTER_GETTERS_METHOD_NAME = 'registerGetters';
const REGISTER_SETTERS_METHOD_NAME = 'registerSetters'; const REGISTER_SETTERS_METHOD_NAME = 'registerSetters';

View File

@ -1,4 +1,4 @@
library angular2.transform.common.options; library angular2.transform.common.options_reader;
import 'package:barback/barback.dart'; import 'package:barback/barback.dart';
import 'options.dart'; import 'options.dart';

View File

@ -43,7 +43,7 @@ class Parser {
Future<List<NgDeps>> _recurse(AssetId id, Future<List<NgDeps>> _recurse(AssetId id,
[List<NgDeps> allDeps, Set<AssetId> seen]) async { [List<NgDeps> allDeps, Set<AssetId> seen]) async {
if (seen == null) seen = new Set<AssetId>(); if (seen == null) seen = new Set<AssetId>();
if (seen.contains(id)) return; if (seen.contains(id)) return null;
seen.add(id); seen.add(id);
if (allDeps == null) allDeps = []; if (allDeps == null) allDeps = [];
@ -75,7 +75,6 @@ class NgDeps {
class _ParseNgDepsVisitor extends Object with RecursiveAstVisitor<Object> { class _ParseNgDepsVisitor extends Object with RecursiveAstVisitor<Object> {
NgDeps ngDeps = null; NgDeps ngDeps = null;
_RegisteredTypeBuilder current = null;
@override @override
Object visitImportDirective(ImportDirective node) { Object visitImportDirective(ImportDirective node) {

View File

@ -1,111 +0,0 @@
library angular2.transform.common;
import 'package:analyzer/analyzer.dart';
import 'package:analyzer/src/generated/java_core.dart';
import 'package:analyzer/src/generated/scanner.dart';
/// Visitor providing common methods for concrete implementations.
class VisitorMixin {
PrintWriter writer;
/**
* Visit the given function body, printing the prefix before if given body is not empty.
*
* @param prefix the prefix to be printed if there is a node to visit
* @param body the function body to be visited
*/
void visitFunctionWithPrefix(String prefix, FunctionBody body) {
if (body is! EmptyFunctionBody) {
writer.print(prefix);
}
visitNode(body);
}
/// Safely visit [node].
void visitNode(AstNode node) {
if (node != null) {
node.accept(this);
}
}
/// Print a list of [nodes] without any separation.
void visitNodeList(NodeList<AstNode> nodes) {
visitNodeListWithSeparator(nodes, "");
}
/// Print a list of [nodes], separated by the given [separator].
void visitNodeListWithSeparator(NodeList<AstNode> nodes, String separator) {
if (nodes != null) {
int size = nodes.length;
for (int i = 0; i < size; i++) {
if (i > 0) {
writer.print(separator);
}
nodes[i].accept(this);
}
}
}
/// Print a list of [nodes], separated by the given [separator] and
/// preceded by the given [prefix].
void visitNodeListWithSeparatorAndPrefix(
String prefix, NodeList<AstNode> nodes, String separator) {
if (nodes != null) {
int size = nodes.length;
if (size > 0) {
writer.print(prefix);
for (int i = 0; i < size; i++) {
if (i > 0) {
writer.print(separator);
}
nodes[i].accept(this);
}
}
}
}
/// Print a list of [nodes], separated by the given [separator] and
/// succeeded by the given [suffix].
void visitNodeListWithSeparatorAndSuffix(
NodeList<AstNode> nodes, String separator, String suffix) {
if (nodes != null) {
int size = nodes.length;
if (size > 0) {
for (int i = 0; i < size; i++) {
if (i > 0) {
writer.print(separator);
}
nodes[i].accept(this);
}
writer.print(suffix);
}
}
}
/// If [node] is null does nothing. Otherwise, prints [prefix], then
/// visits [node].
void visitNodeWithPrefix(String prefix, AstNode node) {
if (node != null) {
writer.print(prefix);
node.accept(this);
}
}
/// If [node] is null does nothing. Otherwise, visits [node], then prints
/// [suffix].
void visitNodeWithSuffix(AstNode node, String suffix) {
if (node != null) {
node.accept(this);
writer.print(suffix);
}
}
/// Safely visit [node], printing [suffix] after the node if it is
/// non-`null`.
void visitTokenWithSuffix(Token token, String suffix) {
if (token != null) {
writer.print(token.lexeme);
writer.print(suffix);
}
}
}

View File

@ -5,10 +5,8 @@ import 'package:dart_style/dart_style.dart';
import 'directive_linker/transformer.dart'; import 'directive_linker/transformer.dart';
import 'directive_processor/transformer.dart'; import 'directive_processor/transformer.dart';
import 'bind_generator/transformer.dart';
import 'reflection_remover/transformer.dart'; import 'reflection_remover/transformer.dart';
import 'common/formatter.dart' as formatter; import 'common/formatter.dart' as formatter;
import 'common/names.dart';
import 'common/options.dart'; import 'common/options.dart';
import 'common/options_reader.dart'; import 'common/options_reader.dart';

View File

@ -17,9 +17,10 @@ Future<String> linkNgDeps(AssetReader reader, AssetId entryPoint) async {
if (ngDeps == null) return null; if (ngDeps == null) return null;
if (ngDeps.imports.isEmpty) return ngDeps.code; if (ngDeps.imports.isEmpty) return ngDeps.code;
var allDeps = ngDeps.imports.toList()..addAll(ngDeps.exports); var depUris = <String>[]
var depList = await _processNgImports( ..addAll(ngDeps.imports.map((i) => i.uri.stringValue))
reader, entryPoint, allDeps.map((node) => node.uri.stringValue)); ..addAll(ngDeps.exports.map((i) => i.uri.stringValue));
var depList = await _processNgImports(reader, entryPoint, depUris);
if (depList.isEmpty) return ngDeps.code; if (depList.isEmpty) return ngDeps.code;

View File

@ -6,7 +6,6 @@ import 'package:angular2/src/transform/common/asset_reader.dart';
import 'package:angular2/src/transform/common/formatter.dart'; import 'package:angular2/src/transform/common/formatter.dart';
import 'package:angular2/src/transform/common/logging.dart' as log; import 'package:angular2/src/transform/common/logging.dart' as log;
import 'package:angular2/src/transform/common/names.dart'; import 'package:angular2/src/transform/common/names.dart';
import 'package:angular2/src/transform/common/options.dart';
import 'package:barback/barback.dart'; import 'package:barback/barback.dart';
import 'linker.dart'; import 'linker.dart';

View File

@ -4,7 +4,6 @@ import 'package:analyzer/analyzer.dart';
import 'package:analyzer/src/generated/java_core.dart'; import 'package:analyzer/src/generated/java_core.dart';
import 'package:angular2/src/transform/common/logging.dart'; import 'package:angular2/src/transform/common/logging.dart';
import 'package:angular2/src/transform/common/names.dart'; import 'package:angular2/src/transform/common/names.dart';
import 'package:angular2/src/transform/common/visitor_mixin.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import 'visitors.dart'; import 'visitors.dart';
@ -27,8 +26,7 @@ String createNgDeps(String code, String path) {
/// Visitor responsible for processing [CompilationUnit] and creating an /// Visitor responsible for processing [CompilationUnit] and creating an
/// associated .ng_deps.dart file. /// associated .ng_deps.dart file.
class CreateNgDepsVisitor extends Object class CreateNgDepsVisitor extends Object with SimpleAstVisitor<Object> {
with SimpleAstVisitor<Object>, VisitorMixin {
final PrintWriter writer; final PrintWriter writer;
final _Tester _tester = const _Tester(); final _Tester _tester = const _Tester();
bool _foundNgDirectives = false; bool _foundNgDirectives = false;
@ -48,11 +46,21 @@ class CreateNgDepsVisitor extends Object
_paramsVisitor = new ParameterTransformVisitor(writer), _paramsVisitor = new ParameterTransformVisitor(writer),
_metaVisitor = new AnnotationsTransformVisitor(writer); _metaVisitor = new AnnotationsTransformVisitor(writer);
void _visitNodeListWithSeparator(NodeList<AstNode> list, String separator) {
if (list == null) return;
for (var i = 0, iLen = list.length; i < iLen; ++i) {
if (i != 0) {
writer.print(separator);
}
list[i].accept(this);
}
}
@override @override
void visitCompilationUnit(CompilationUnit node) { Object visitCompilationUnit(CompilationUnit node) {
visitNodeListWithSeparator(node.directives, " "); _visitNodeListWithSeparator(node.directives, " ");
_openFunctionWrapper(); _openFunctionWrapper();
visitNodeListWithSeparator(node.declarations, " "); _visitNodeListWithSeparator(node.declarations, " ");
_closeFunctionWrapper(); _closeFunctionWrapper();
return null; return null;
} }
@ -139,7 +147,7 @@ class CreateNgDepsVisitor extends Object
_foundNgDirectives = true; _foundNgDirectives = true;
} }
writer.print('..registerType('); writer.print('..registerType(');
visitNode(node.name); node.name.accept(this);
writer.print(''', {'factory': '''); writer.print(''', {'factory': ''');
if (ctor == null) { if (ctor == null) {
_generateEmptyFactory(node.name.toString()); _generateEmptyFactory(node.name.toString());
@ -155,9 +163,8 @@ class CreateNgDepsVisitor extends Object
writer.print(''', 'annotations': '''); writer.print(''', 'annotations': ''');
node.accept(_metaVisitor); node.accept(_metaVisitor);
writer.print('})'); writer.print('})');
return null;
} }
return null;
} }
Object _nodeToSource(AstNode node) { Object _nodeToSource(AstNode node) {

View File

@ -3,10 +3,9 @@ library angular2.transform.directive_processor;
import 'package:analyzer/analyzer.dart'; import 'package:analyzer/analyzer.dart';
import 'package:analyzer/src/generated/java_core.dart'; import 'package:analyzer/src/generated/java_core.dart';
import 'package:angular2/src/transform/common/logging.dart'; import 'package:angular2/src/transform/common/logging.dart';
import 'package:angular2/src/transform/common/visitor_mixin.dart';
/// SourceVisitor designed to accept [ConstructorDeclaration] nodes. /// `ToSourceVisitor` designed to accept [ConstructorDeclaration] nodes.
class _CtorTransformVisitor extends ToSourceVisitor with VisitorMixin { class _CtorTransformVisitor extends ToSourceVisitor {
bool _withParameterAnnotations = true; bool _withParameterAnnotations = true;
bool _withParameterTypes = true; bool _withParameterTypes = true;
bool _withParameterNames = true; bool _withParameterNames = true;
@ -20,25 +19,50 @@ class _CtorTransformVisitor extends ToSourceVisitor with VisitorMixin {
: this.writer = writer, : this.writer = writer,
super(writer); super(writer);
void _visitNodeWithPrefix(String prefix, AstNode node) {
if (node != null) {
writer.print(prefix);
node.accept(this);
}
}
void _visitNodeWithSuffix(AstNode node, String suffix) {
if (node != null) {
node.accept(this);
writer.print(suffix);
}
}
void _visitNode(AstNode node) {
if (node != null) {
node.accept(this);
}
}
/// If [_withParameterTypes] is true, this method outputs [node]'s type. If /// If [_withParameterTypes] is true, this method outputs [node]'s type. If
/// [_withParameterNames] is true, this method outputs [node]'s identifier. /// [_withParameterNames] is true, this method outputs [node]'s identifier.
Object _visitNormalFormalParameter( Object _visitNormalFormalParameter(
NodeList<Annotation> metadata, TypeName type, SimpleIdentifier name) { NodeList<Annotation> metadata, TypeName type, SimpleIdentifier name) {
if (_withParameterAnnotations && metadata != null) { if (_withParameterAnnotations && metadata != null) {
assert(_withParameterTypes); assert(_withParameterTypes);
var suffix = type != null ? ', ' : ''; for (var i = 0, iLen = metadata.length; i < iLen; ++i) {
visitNodeListWithSeparatorAndSuffix(metadata, ', ', suffix); if (i != 0) {
writer.print(', ');
}
metadata[i].accept(this);
}
writer.print(type != null && metadata.isNotEmpty ? ', ' : '');
} }
var needCompileTimeConstants = !_withParameterNames; var needCompileTimeConstants = !_withParameterNames;
if (_withParameterTypes && type != null) { if (_withParameterTypes && type != null) {
visitNodeWithSuffix(type.name, ' '); _visitNodeWithSuffix(type.name, ' ');
if (!needCompileTimeConstants) { if (!needCompileTimeConstants) {
// Types with arguments are not compile-time constants. // Types with arguments are not compile-time constants.
visitNodeWithSuffix(type.typeArguments, ' '); _visitNodeWithSuffix(type.typeArguments, ' ');
} }
} }
if (_withParameterNames) { if (_withParameterNames) {
visitNode(name); _visitNode(name);
} }
return null; return null;
} }
@ -87,7 +111,7 @@ class _CtorTransformVisitor extends ToSourceVisitor with VisitorMixin {
@override @override
Object visitDefaultFormalParameter(DefaultFormalParameter node) { Object visitDefaultFormalParameter(DefaultFormalParameter node) {
visitNode(node.parameter); _visitNode(node.parameter);
// Ignore the declared default value. // Ignore the declared default value.
return null; return null;
} }
@ -112,9 +136,9 @@ class _CtorTransformVisitor extends ToSourceVisitor with VisitorMixin {
Object visitAnnotation(Annotation node) { Object visitAnnotation(Annotation node) {
var prefix = var prefix =
node.arguments != null && node.arguments.length > 0 ? 'const ' : ''; node.arguments != null && node.arguments.length > 0 ? 'const ' : '';
visitNodeWithPrefix(prefix, node.name); _visitNodeWithPrefix(prefix, node.name);
visitNodeWithPrefix(".", node.constructorName); _visitNodeWithPrefix(".", node.constructorName);
visitNode(node.arguments); _visitNode(node.arguments);
return null; return null;
} }
} }
@ -132,7 +156,7 @@ class ParameterTransformVisitor extends _CtorTransformVisitor {
Object visitConstructorDeclaration(ConstructorDeclaration node) { Object visitConstructorDeclaration(ConstructorDeclaration node) {
_buildFieldMap(node); _buildFieldMap(node);
writer.print('const ['); writer.print('const [');
visitNode(node.parameters); _visitNode(node.parameters);
writer.print(']'); writer.print(']');
return null; return null;
} }
@ -140,8 +164,7 @@ class ParameterTransformVisitor extends _CtorTransformVisitor {
@override @override
Object visitFormalParameterList(FormalParameterList node) { Object visitFormalParameterList(FormalParameterList node) {
NodeList<FormalParameter> parameters = node.parameters; NodeList<FormalParameter> parameters = node.parameters;
int size = parameters.length; for (int i = 0, iLen = parameters.length; i < iLen; i++) {
for (int i = 0; i < size; i++) {
if (i > 0) { if (i > 0) {
writer.print(', '); writer.print(', ');
} }
@ -166,20 +189,20 @@ class FactoryTransformVisitor extends _CtorTransformVisitor {
_buildFieldMap(node); _buildFieldMap(node);
_withParameterNames = true; _withParameterNames = true;
_withParameterTypes = true; _withParameterTypes = true;
visitNode(node.parameters); _visitNode(node.parameters);
writer.print(' => new '); writer.print(' => new ');
visitNode(node.returnType); _visitNode(node.returnType);
visitNodeWithPrefix(".", node.name); _visitNodeWithPrefix(".", node.name);
_withParameterTypes = false; _withParameterTypes = false;
visitNode(node.parameters); _visitNode(node.parameters);
return null; return null;
} }
} }
/// ToSourceVisitor designed to print a [ClassDeclaration] node as a /// ToSourceVisitor designed to print a [ClassDeclaration] node as a
/// 'annotations' value for Angular2's [registerType] calls. /// 'annotations' value for Angular2's [registerType] calls.
class AnnotationsTransformVisitor extends ToSourceVisitor with VisitorMixin { class AnnotationsTransformVisitor extends ToSourceVisitor {
final PrintWriter writer; final PrintWriter writer;
AnnotationsTransformVisitor(PrintWriter writer) AnnotationsTransformVisitor(PrintWriter writer)
: this.writer = writer, : this.writer = writer,
@ -202,9 +225,16 @@ class AnnotationsTransformVisitor extends ToSourceVisitor with VisitorMixin {
@override @override
Object visitAnnotation(Annotation node) { Object visitAnnotation(Annotation node) {
writer.print('const '); writer.print('const ');
visitNode(node.name); if (node.name != null) {
visitNodeWithPrefix(".", node.constructorName); node.name.accept(this);
visitNode(node.arguments); }
if (node.constructorName != null) {
writer.print('.');
node.constructorName.accept(this);
}
if (node.arguments != null) {
node.arguments.accept(this);
}
return null; return null;
} }
} }

View File

@ -1,71 +0,0 @@
library angular2.transform;
import 'dart:collection' show Queue;
import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/element.dart';
import 'resolvers.dart';
/// Provides a mechanism for checking an element for the provided
/// [_annotationClass] and reporting the resulting (element, annotation) pairs.
class AnnotationMatcher {
/// Queue for annotations.
final matchQueue = new Queue<AnnotationMatch>();
/// All the annotations we have seen for each element
final _seenAnnotations = new Map<Element, Set<ElementAnnotation>>();
/// The classes we are searching for to populate [matchQueue].
final Set<ClassElement> _annotationClasses;
AnnotationMatcher(this._annotationClasses);
/// Records all [_annotationClass] annotations and the [element]s they apply to.
/// Returns
List<AnnotationMatch> processAnnotations(ClassElement element) {
// Finding the node corresponding to [element] can be very expensive.
ClassDeclaration cachedNode = null;
var result = <AnnotationMatch>[];
element.metadata.where((ElementAnnotation meta) {
// TODO(tjblasi): Make this recognize non-ConstructorElement annotations.
return meta.element is ConstructorElement &&
_isAnnotationMatch(meta.element.returnType);
}).where((ElementAnnotation meta) {
// Only process ([element], [meta]) combinations we haven't seen previously.
return !_seenAnnotations
.putIfAbsent(element, () => new Set<ElementAnnotation>())
.contains(meta);
}).forEach((ElementAnnotation meta) {
if (cachedNode == null) {
cachedNode = element.node;
}
var match = new AnnotationMatch(cachedNode, meta);
_seenAnnotations[element].add(meta);
matchQueue.addLast(match);
result.add(match);
});
return result;
}
/// Whether [type], its superclass, or one of its interfaces matches [_annotationClass].
bool _isAnnotationMatch(InterfaceType type) {
return _annotationClasses.any((el) => isAnnotationMatch(type, el));
}
}
/// [ClassElement] / [ElementAnnotation] pair.
class AnnotationMatch {
/// The resolved element corresponding to [node].
final ClassElement element;
/// The Ast node corresponding to the class definition.
final ClassDeclaration node;
/// The resolved element for the matched annotation.
final ElementAnnotation annotation;
AnnotationMatch(ClassDeclaration node, this.annotation)
: node = node,
element = node.element;
}

View File

@ -1,518 +0,0 @@
library angular2.transform;
import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/element.dart';
import 'package:analyzer/src/generated/java_core.dart';
import 'package:barback/barback.dart' show AssetId, TransformLogger;
import 'package:dart_style/dart_style.dart';
import 'package:path/path.dart' as path;
import 'annotation_processor.dart';
import 'common/logging.dart';
/// Base class that maintains codegen state.
class Context {
/// Maps libraries to the import prefixes we will use in the newly
/// generated code.
final Map<LibraryElement, String> _libraryPrefixes = {};
/// Whether to generate constructor stubs for classes annotated
/// with [Component], [Decorator], [View], and [Inject] (and subtypes).
bool generateCtorStubs = true;
/// Whether to generate setter stubs for classes annotated with
/// [Directive] subtypes. These setters depend on the value passed to the
/// annotation's `properties` value.
bool generateSetterStubs = true;
DirectiveRegistry _directiveRegistry;
/// Generates [registerType] calls for all [register]ed [AnnotationMatch]
/// objects.
DirectiveRegistry get directiveRegistry => _directiveRegistry;
Context() {
_directiveRegistry = new _DirectiveRegistryImpl(this);
}
/// If elements in [lib] should be prefixed in our generated code, returns
/// the appropriate prefix followed by a `.`. Future items from the same
/// library will use the same prefix.
/// If [lib] does not need a prefix, returns the empty string.
String _getPrefixDot(LibraryElement lib) {
if (lib == null || lib.isInSdk) return '';
var prefix =
_libraryPrefixes.putIfAbsent(lib, () => 'i${_libraryPrefixes.length}');
return '${prefix}.';
}
}
/// Object which [register]s [AnnotationMatch] objects for code generation.
abstract class DirectiveRegistry {
// Adds [entry] to the `registerType` calls which will be generated.
void register(AnnotationMatch entry);
}
const setupReflectionMethodName = 'setupReflection';
const _libraryDeclaration = '''
library angular2.transform.generated;
''';
const _reflectorImport = '''
import 'package:angular2/src/reflection/reflection.dart' show reflector;
''';
/// Default implementation to map from [LibraryElement] to [AssetId]. This
/// assumes that [el.source] has a getter called [assetId].
AssetId _assetIdFromLibraryElement(LibraryElement el) {
return (el.source as dynamic).assetId;
}
String codegenEntryPoint(Context context, {AssetId newEntryPoint}) {
if (newEntryPoint == null) {
throw new ArgumentError.notNull('newEntryPoint');
}
// TODO(kegluneq): copyright declaration
var outBuffer = new StringBuffer()
..write(_libraryDeclaration)
..write(_reflectorImport);
_codegenImports(context, newEntryPoint, outBuffer);
outBuffer
.write('${setupReflectionMethodName}() {${context.directiveRegistry}}');
return new DartFormatter().format(outBuffer.toString());
}
void _codegenImports(
Context context, AssetId newEntryPoint, StringBuffer buffer) {
context._libraryPrefixes.forEach((lib, prefix) {
buffer
..write(_codegenImport(
context, _assetIdFromLibraryElement(lib), newEntryPoint))
..writeln('as ${prefix};');
});
}
String _codegenImport(Context context, AssetId libraryId, AssetId entryPoint) {
if (libraryId.path.startsWith('lib/')) {
var packagePath = libraryId.path.replaceFirst('lib/', '');
return "import 'package:${libraryId.package}/${packagePath}'";
} else if (libraryId.package != entryPoint.package) {
logger.error("Can't import `${libraryId}` from `${entryPoint}`");
} else if (path.url.split(libraryId.path)[0] ==
path.url.split(entryPoint.path)[0]) {
var relativePath =
path.relative(libraryId.path, from: path.dirname(entryPoint.path));
return "import '${relativePath}'";
} else {
logger.error("Can't import `${libraryId}` from `${entryPoint}`");
}
}
// TODO(https://github.com/kegluneq/angular/issues/4): Remove calls to
// Element#node.
class _DirectiveRegistryImpl implements DirectiveRegistry {
final Context _context;
final PrintWriter _writer;
final Set<ClassDeclaration> _seen = new Set();
final _AnnotationsTransformVisitor _annotationsVisitor;
final _BindTransformVisitor _bindVisitor;
final _FactoryTransformVisitor _factoryVisitor;
final _ParameterTransformVisitor _parametersVisitor;
_DirectiveRegistryImpl._internal(Context context, PrintWriter writer)
: _writer = writer,
_context = context,
_annotationsVisitor = new _AnnotationsTransformVisitor(writer, context),
_bindVisitor = new _BindTransformVisitor(writer, context),
_factoryVisitor = new _FactoryTransformVisitor(writer, context),
_parametersVisitor = new _ParameterTransformVisitor(writer, context);
factory _DirectiveRegistryImpl(Context context) {
return new _DirectiveRegistryImpl._internal(
context, new PrintStringWriter());
}
@override
String toString() {
return _seen.isEmpty ? '' : 'reflector${_writer};';
}
// Adds [entry] to the `registerType` calls which will be generated.
void register(AnnotationMatch entry) {
if (_seen.contains(entry.node)) return;
_seen.add(entry.node);
if (_context.generateCtorStubs) {
_generateCtorStubs(entry);
}
if (_context.generateSetterStubs) {
_generateSetterStubs(entry);
}
}
void _generateSetterStubs(AnnotationMatch entry) {
// TODO(kegluneq): Remove these requirements for setter stub generation.
if (entry.element is! ClassElement) {
logger.error('Directives can only be applied to classes.');
return;
}
if (entry.node is! ClassDeclaration) {
logger.error('Unsupported annotation type for ctor stub generation. '
'Only class declarations are supported as Directives.');
return;
}
entry.node.accept(_bindVisitor);
}
void _generateCtorStubs(AnnotationMatch entry) {
var element = entry.element;
var annotation = entry.annotation;
// TODO(kegluneq): Remove these requirements for ctor stub generation.
if (annotation.element is! ConstructorElement) {
logger.error('Unsupported annotation type for ctor stub generation. '
'Only constructors are supported as Directives.');
return;
}
if (element is! ClassElement) {
logger.error('Directives can only be applied to classes.');
return;
}
if (entry.node is! ClassDeclaration) {
logger.error('Unsupported annotation type for ctor stub generation. '
'Only class declarations are supported as Directives.');
return;
}
var ctor = element.unnamedConstructor;
if (ctor == null) {
logger.error('No unnamed constructor found for ${element.name}');
return;
}
var ctorNode = ctor.node;
_writer.print('..registerType(');
_codegenClassTypeString(element);
_writer.print(', {"factory": ');
_codegenClassTypeString(element);
_writer.print('.ngFactory');
_writer.print(', "parameters": ');
_codegenClassTypeString(element);
_writer.print('.ngParameters');
_writer.print(', "annotations": ');
_codegenClassTypeString(element);
_writer.print('.ngAnnotations');
_writer.print('})');
}
void _codegenClassTypeString(ClassElement el) {
_writer.print('${_context._getPrefixDot(el.library)}${el.name}');
}
/// Creates the 'annotations' property for the Angular2 [registerType] call
/// for [node].
void _codegenAnnotationsProp(ClassDeclaration node) {
node.accept(_annotationsVisitor);
}
/// Creates the 'factory' property for the Angular2 [registerType] call
/// for [node]. [element] is necessary if [node] is null.
void _codegenFactoryProp(ConstructorDeclaration node, ClassElement element) {
if (node == null) {
// This occurs when the class does not declare a constructor.
var prefix = _context._getPrefixDot(element.library);
_writer.print('() => new ${prefix}${element.displayName}()');
} else {
node.accept(_factoryVisitor);
}
}
/// Creates the 'parameters' property for the Angular2 [registerType] call
/// for [node].
void _codegenParametersProp(ConstructorDeclaration node) {
if (node == null) {
// This occurs when the class does not declare a constructor.
_writer.print('const [const []]');
} else {
node.accept(_parametersVisitor);
}
}
}
/// Visitor providing common methods for concrete implementations.
class _TransformVisitorMixin {
final Context context;
final PrintWriter writer;
/// Safely visit [node].
void _visitNode(AstNode node) {
if (node != null) {
node.accept(this);
}
}
/// If [node] is null does nothing. Otherwise, prints [prefix], then
/// visits [node].
void _visitNodeWithPrefix(String prefix, AstNode node) {
if (node != null) {
writer.print(prefix);
node.accept(this);
}
}
/// If [node] is null does nothing. Otherwise, visits [node], then prints
/// [suffix].
void _visitNodeWithSuffix(AstNode node, String suffix) {
if (node != null) {
node.accept(this);
writer.print(suffix);
}
}
String prefixedSimpleIdentifier(SimpleIdentifier node) {
// Make sure the identifier is prefixed if necessary.
if (node.bestElement is ClassElementImpl ||
node.bestElement is PropertyAccessorElement) {
return context._getPrefixDot(node.bestElement.library) +
node.token.lexeme;
} else {
return node.token.lexeme;
}
}
}
class _TransformVisitor extends ToSourceVisitor with _TransformVisitorMixin {
final Context context;
final PrintWriter writer;
_TransformVisitor(PrintWriter writer, this.context)
: this.writer = writer,
super(writer);
@override
Object visitPrefixedIdentifier(PrefixedIdentifier node) {
// We add our own prefixes in [visitSimpleIdentifier], discard any used in
// the original source.
writer.print(super.prefixedSimpleIdentifier(node.identifier));
return null;
}
@override
Object visitSimpleIdentifier(SimpleIdentifier node) {
writer.print(super.prefixedSimpleIdentifier(node));
return null;
}
}
/// SourceVisitor designed to accept [ConstructorDeclaration] nodes.
class _CtorTransformVisitor extends _TransformVisitor {
bool _withParameterTypes = true;
bool _withParameterNames = true;
_CtorTransformVisitor(PrintWriter writer, Context _context)
: super(writer, _context);
/// If [_withParameterTypes] is true, this method outputs [node]'s type
/// (appropriately prefixed based on [_libraryPrefixes]. If
/// [_withParameterNames] is true, this method outputs [node]'s identifier.
Object _visitNormalFormalParameter(NormalFormalParameter node) {
if (_withParameterTypes) {
var paramType = node.element.type;
var prefix = context._getPrefixDot(paramType.element.library);
writer.print('${prefix}${paramType.displayName}');
if (_withParameterNames) {
_visitNodeWithPrefix(' ', node.identifier);
}
} else if (_withParameterNames) {
_visitNode(node.identifier);
}
return null;
}
@override
Object visitSimpleFormalParameter(SimpleFormalParameter node) {
return _visitNormalFormalParameter(node);
}
@override
Object visitFieldFormalParameter(FieldFormalParameter node) {
if (node.parameters != null) {
logger.error('Parameters in ctor not supported '
'(${super.visitFormalParameterList(node)}');
}
return _visitNormalFormalParameter(node);
}
@override
Object visitDefaultFormalParameter(DefaultFormalParameter node) {
_visitNode(node.parameter);
// Ignore the declared default value.
return null;
}
@override
/// Overridden to avoid outputting grouping operators for default parameters.
Object visitFormalParameterList(FormalParameterList node) {
writer.print('(');
NodeList<FormalParameter> parameters = node.parameters;
int size = parameters.length;
for (int i = 0; i < size; i++) {
if (i > 0) {
writer.print(', ');
}
parameters[i].accept(this);
}
writer.print(')');
return null;
}
}
/// ToSourceVisitor designed to print 'parameters' values for Angular2's
/// [registerType] calls.
class _ParameterTransformVisitor extends _CtorTransformVisitor {
_ParameterTransformVisitor(PrintWriter writer, Context _context)
: super(writer, _context);
@override
Object visitConstructorDeclaration(ConstructorDeclaration node) {
_withParameterNames = false;
_withParameterTypes = true;
writer.print('const [const [');
_visitNode(node.parameters);
writer.print(']]');
return null;
}
@override
Object visitFormalParameterList(FormalParameterList node) {
NodeList<FormalParameter> parameters = node.parameters;
int size = parameters.length;
for (int i = 0; i < size; i++) {
if (i > 0) {
writer.print(', ');
}
parameters[i].accept(this);
}
return null;
}
}
/// ToSourceVisitor designed to print 'factory' values for Angular2's
/// [registerType] calls.
class _FactoryTransformVisitor extends _CtorTransformVisitor {
_FactoryTransformVisitor(PrintWriter writer, Context _context)
: super(writer, _context);
@override
Object visitConstructorDeclaration(ConstructorDeclaration node) {
_withParameterNames = true;
_withParameterTypes = true;
_visitNode(node.parameters);
writer.print(' => new ');
_visitNode(node.returnType);
_visitNodeWithPrefix(".", node.name);
_withParameterTypes = false;
_visitNode(node.parameters);
return null;
}
}
/// ToSourceVisitor designed to print a [ClassDeclaration] node as a
/// 'annotations' value for Angular2's [registerType] calls.
class _AnnotationsTransformVisitor extends _TransformVisitor {
_AnnotationsTransformVisitor(PrintWriter writer, Context _context)
: super(writer, _context);
@override
Object visitClassDeclaration(ClassDeclaration node) {
writer.print('const [');
var size = node.metadata.length;
for (var i = 0; i < size; ++i) {
if (i > 0) {
writer.print(', ');
}
node.metadata[i].accept(this);
}
writer.print(']');
return null;
}
@override
Object visitAnnotation(Annotation node) {
writer.print('const ');
_visitNode(node.name);
// TODO(tjblasi): Do we need to handle named constructors for annotations?
// _visitNodeWithPrefix(".", node.constructorName);
_visitNode(node.arguments);
return null;
}
}
/// Visitor designed to print a [ClassDeclaration] node as a
/// `registerSetters` call for Angular2.
class _BindTransformVisitor extends Object
with SimpleAstVisitor<Object>, _TransformVisitorMixin {
final Context context;
final PrintWriter writer;
final List<String> _bindPieces = [];
SimpleIdentifier _currentName = null;
_BindTransformVisitor(this.writer, this.context);
@override
Object visitClassDeclaration(ClassDeclaration node) {
_currentName = node.name;
node.metadata.forEach((meta) => _visitNode(meta));
if (_bindPieces.isNotEmpty) {
writer.print('..registerSetters({${_bindPieces.join(', ')}})');
}
return null;
}
@override
Object visitAnnotation(Annotation node) {
// TODO(kegluneq): Remove this restriction.
if (node.element is ConstructorElement) {
if (node.element.returnType.element is ClassElement) {
// TODO(kegluneq): Check if this is actually a `directive`.
node.arguments.arguments.forEach((arg) => _visitNode(arg));
}
}
return null;
}
@override
Object visitNamedExpression(NamedExpression node) {
if (node.name.label.toString() == 'bind') {
// TODO(kegluneq): Remove this restriction.
if (node.expression is MapLiteral) {
node.expression.accept(this);
}
}
return null;
}
@override
Object visitMapLiteral(MapLiteral node) {
node.entries.forEach((entry) {
if (entry.key is SimpleStringLiteral) {
_visitNode(entry.key);
} else {
logger.error('`properties` currently only supports string literals');
}
});
return null;
}
@override
Object visitSimpleStringLiteral(SimpleStringLiteral node) {
if (_currentName == null) {
logger.error('Unexpected code path: `currentName` should never be null');
}
_bindPieces.add('"${node.value}": ('
'${super.prefixedSimpleIdentifier(_currentName)} o, String value) => '
'o.${node.value} = value');
return null;
}
}

View File

@ -1,93 +0,0 @@
library angular2.transform;
import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/element.dart';
import 'package:code_transformers/resolver.dart';
import 'resolvers.dart';
/// Finds all calls to the Angular2 [bootstrap] method defined in [library].
/// This only searches the code defined in the file
// represented by [library], not `part`s, `import`s, `export`s, etc.
Set<BootstrapCallInfo> findBootstrapCalls(
Resolver resolver, LibraryElement library) {
var types = new Angular2Types(resolver);
if (types.bootstrapMethod == null) {
throw new ArgumentError(
'Could not find symbol for ${bootstrapMethodName}.');
}
var visitor = new _FindFunctionVisitor(types.bootstrapMethod);
// TODO(kegluneq): Determine how to get nodes without querying Element#node.
// Root of file defining that library (main part).
library.definingCompilationUnit.node.accept(visitor);
return new Set.from(visitor.functionCalls.map((MethodInvocation mi) {
var visitor = new _ParseBootstrapTypeVisitor(types);
if (mi.argumentList.arguments.isEmpty) {
throw new ArgumentError('No arguments provided to `bootstrap`.');
}
mi.argumentList.arguments[0].accept(visitor);
if (visitor.bootstrapType == null) {
throw new UnsupportedError(
'Failed to parse `bootstrap` call: ${mi.toSource()}');
}
return new BootstrapCallInfo(mi, visitor.bootstrapType);
}));
}
/// Information about a single call to Angular2's [bootstrap] method.
class BootstrapCallInfo {
/// The [AstNode] representing the call to [bootstrap].
final MethodInvocation call;
/// The type, which should be annotated as a [Component], which is the root
/// of the Angular2 app.
final ClassElement bootstrapType;
BootstrapCallInfo(this.call, this.bootstrapType);
}
/// Visitor that finds the Angular2 bootstrap component given [bootstrap]'s
/// first argument.
///
/// This visitor does not recursively visit nodes in the Ast.
class _ParseBootstrapTypeVisitor extends SimpleAstVisitor<Object> {
ClassElement bootstrapType = null;
final Angular2Types _types;
_ParseBootstrapTypeVisitor(this._types);
// TODO(kegluneq): Allow non-SimpleIdentifier expressions.
@override
Object visitSimpleIdentifier(SimpleIdentifier node) {
bootstrapType = (node.bestElement as ClassElement);
if (!_types.isComponent(bootstrapType)) {
throw new ArgumentError('Class passed to `${bootstrapMethodName}` must '
'be a @${_types.componentAnnotation.name}');
}
}
}
/// Recursively visits all nodes in an Ast structure, recording all encountered
/// calls to the provided [FunctionElement].
class _FindFunctionVisitor extends RecursiveAstVisitor<Object> {
final FunctionElement _target;
_FindFunctionVisitor(this._target);
final Set<MethodInvocation> functionCalls = new Set();
bool _isDesiredMethod(MethodInvocation node) {
return node.methodName.bestElement == _target;
}
@override
Object visitMethodInvocation(MethodInvocation node) {
if (_isDesiredMethod(node)) {
functionCalls.add(node);
}
return super.visitMethodInvocation(node);
}
}

View File

@ -1,152 +0,0 @@
library angular2.transform;
import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/element.dart';
import 'package:analyzer/src/generated/java_core.dart';
import 'package:barback/barback.dart';
import 'package:code_transformers/resolver.dart';
import 'package:dart_style/dart_style.dart';
import 'package:path/path.dart' as path;
import 'codegen.dart';
import 'common/logging.dart';
import 'resolvers.dart';
/// Finds all calls to the Angular2 [ReflectionCapabilities] constructor
/// defined in [library].
/// This only searches the code defined in the file
// represented by [library], not `part`s, `import`s, `export`s, etc.
String findReflectionCapabilities(
Resolver resolver, AssetId reflectionEntryPoint, AssetId newEntryPoint) {
var types = new Angular2Types(resolver);
if (types.reflectionCapabilities == null) {
throw new ArgumentError(
'Could not find class for ${reflectionCapabilitiesTypeName}.');
}
var codegen = new _SetupReflectionCodegen(
resolver, reflectionEntryPoint, newEntryPoint);
var writer = new PrintStringWriter();
var visitor = new _RewriteReflectionEntryPointVisitor(
writer, types.reflectionCapabilities, codegen);
// TODO(kegluneq): Determine how to get nodes without querying Element#node.
// Root of file defining that library (main part).
resolver.getLibrary(reflectionEntryPoint).definingCompilationUnit.node
.accept(visitor);
return new DartFormatter().format(writer.toString());
}
class _SetupReflectionCodegen {
static const _prefixBase = 'ngStaticInit';
final String prefix;
final String importUri;
_SetupReflectionCodegen._internal(this.prefix, this.importUri);
factory _SetupReflectionCodegen(
Resolver resolver, AssetId reflectionEntryPoint, AssetId newEntryPoint) {
var lib = resolver.getLibrary(reflectionEntryPoint);
var prefix = _prefixBase;
var idx = 0;
while (lib.imports.any((import) {
return import.prefix != null && import.prefix == prefix;
})) {
prefix = '${_prefixBase}${idx++}';
}
var importPath = path.relative(newEntryPoint.path,
from: path.dirname(reflectionEntryPoint.path));
return new _SetupReflectionCodegen._internal(prefix, importPath);
}
/// Generates code to import the library containing the method which sets up
/// Angular2 reflection statically.
///
/// The code generated here should follow the example of code generated for
/// an [ImportDirective] node.
String codegenImport() {
return 'import \'${importUri}\' as ${prefix};';
}
/// Generates code to call the method which sets up Angular2 reflection
/// statically.
///
/// The code generated here should follow the example of code generated for
/// a [MethodInvocation] node, that is, it should be prefixed as necessary
/// and not be followed by a ';'.
String codegenSetupReflectionCall() {
return '${prefix}.${setupReflectionMethodName}()';
}
}
class _RewriteReflectionEntryPointVisitor extends ToSourceVisitor {
final PrintWriter _writer;
final ClassElement _forbiddenClass;
final _SetupReflectionCodegen _codegen;
_RewriteReflectionEntryPointVisitor(
PrintWriter writer, this._forbiddenClass, this._codegen)
: _writer = writer,
super(writer);
bool _isNewReflectionCapabilities(InstanceCreationExpression node) {
var typeElement = node.constructorName.type.name.bestElement;
return typeElement != null && typeElement == _forbiddenClass;
}
bool _isReflectionCapabilitiesImport(ImportDirective node) {
return node.uriElement == _forbiddenClass.library;
}
@override
Object visitImportDirective(ImportDirective node) {
if (_isReflectionCapabilitiesImport(node)) {
// TODO(kegluneq): Remove newlines once dart_style bug is fixed.
// https://github.com/dart-lang/dart_style/issues/178
// _writer.print('\n/* ReflectionCapabilities import removed */\n');
_writer.print(_codegen.codegenImport());
// TODO(kegluneq): Remove once we generate all needed code.
{
super.visitImportDirective(node);
}
return null;
}
return super.visitImportDirective(node);
}
@override
Object visitAssignmentExpression(AssignmentExpression node) {
if (node.rightHandSide is InstanceCreationExpression &&
_isNewReflectionCapabilities(node.rightHandSide)) {
// TODO(kegluneq): Remove newlines once dart_style bug is fixed.
// https://github.com/dart-lang/dart_style/issues/178
// _writer.print('/* Creation of ReflectionCapabilities removed */\n');
_writer.print(_codegen.codegenSetupReflectionCall());
// TODO(kegluneq): Remove once we generate all needed code.
{
_writer.print(';');
node.leftHandSide.accept(this);
_writer.print(' ${node.operator.lexeme} ');
super.visitInstanceCreationExpression(node.rightHandSide);
}
return null;
}
return super.visitAssignmentExpression(node);
}
@override
Object visitInstanceCreationExpression(InstanceCreationExpression node) {
if (_isNewReflectionCapabilities(node)) {
logger.error('Unexpected format in creation of '
'${reflectionCapabilitiesTypeName}');
} else {
return super.visitInstanceCreationExpression(node);
}
return null;
}
}

View File

@ -1,140 +0,0 @@
library angular2.transform;
import 'package:barback/barback.dart';
import 'package:analyzer/src/generated/element.dart';
import 'package:code_transformers/resolver.dart';
Resolvers createResolvers() {
return new Resolvers.fromMock({
// The list of types below is derived from:
// * types that are used internally by the resolver (see
// _initializeFrom in resolver.dart).
// TODO(jakemac): Move this into code_transformers so it can be shared.
'dart:core': '''
library dart.core;
class Object {}
class Function {}
class StackTrace {}
class Symbol {}
class Type {}
class String extends Object {}
class bool extends Object {}
class num extends Object {}
class int extends num {}
class double extends num {}
class DateTime extends Object {}
class Null extends Object {}
class Deprecated extends Object {
final String expires;
const Deprecated(this.expires);
}
const Object deprecated = const Deprecated("next release");
class _Override { const _Override(); }
const Object override = const _Override();
class _Proxy { const _Proxy(); }
const Object proxy = const _Proxy();
class List<V> extends Object {}
class Map<K, V> extends Object {}
''',
'dart:html': '''
library dart.html;
class HtmlElement {}
''',
});
}
const bootstrapMethodName = 'bootstrap';
const reflectionCapabilitiesTypeName = 'ReflectionCapabilities';
/// Provides resolved [Elements] for well-known Angular2 symbols.
class Angular2Types {
static Map<Resolver, Angular2Types> _cache = {};
static final _annotationsLibAssetId =
new AssetId('angular2', 'lib/src/core/annotations/annotations.dart');
static final _applicationLibAssetId =
new AssetId('angular2', 'lib/src/core/application.dart');
static final _templateLibAssetId =
new AssetId('angular2', 'lib/src/core/annotations/view.dart');
static final _reflectionCapabilitiesLibAssetId = new AssetId(
'angular2', 'lib/src/reflection/reflection_capabilities.dart');
final Resolver _resolver;
FunctionElement _bootstrapMethod;
Angular2Types._internal(this._resolver);
factory Angular2Types(Resolver resolver) {
return _cache.putIfAbsent(
resolver, () => new Angular2Types._internal(resolver));
}
LibraryElement get annotationsLib =>
_resolver.getLibrary(_annotationsLibAssetId);
ClassElement get directiveAnnotation =>
_getTypeSafe(annotationsLib, 'Directive');
ClassElement get componentAnnotation =>
_getTypeSafe(annotationsLib, 'Component');
ClassElement get decoratorAnnotation =>
_getTypeSafe(annotationsLib, 'Decorator');
LibraryElement get templateLib => _resolver.getLibrary(_templateLibAssetId);
ClassElement get templateAnnotation => _getTypeSafe(templateLib, 'View');
LibraryElement get reflectionCapabilitiesLib =>
_resolver.getLibrary(_reflectionCapabilitiesLibAssetId);
ClassElement get reflectionCapabilities =>
_getTypeSafe(reflectionCapabilitiesLib, reflectionCapabilitiesTypeName);
LibraryElement get applicationLib =>
_resolver.getLibrary(_applicationLibAssetId);
FunctionElement get bootstrapMethod {
if (_bootstrapMethod == null) {
_bootstrapMethod = applicationLib.definingCompilationUnit.functions
.firstWhere((FunctionElement el) => el.name == bootstrapMethodName,
orElse: () => null);
}
return _bootstrapMethod;
}
/// Gets the type named [name] in library [lib]. Returns `null` if [lib] is
/// `null` or [name] cannot be found in [lib].
ClassElement _getTypeSafe(LibraryElement lib, String name) {
if (lib == null) return null;
return lib.getType(name);
}
/// Whether [clazz] is annotated as a [Component].
bool isComponent(ClassElement clazz) =>
hasAnnotation(clazz, componentAnnotation);
}
/// Whether [type], its superclass, or one of its interfaces matches [target].
bool isAnnotationMatch(InterfaceType type, ClassElement target) {
if (type == null || type.element == null) return false;
if (type.element.type == target.type) return true;
if (isAnnotationMatch(type.superclass, target)) return true;
for (var interface in type.interfaces) {
if (isAnnotationMatch(interface, target)) return true;
}
return false;
}
/// Determines whether [clazz] has at least one annotation that `is` a
/// [metaClazz].
bool hasAnnotation(ClassElement clazz, ClassElement metaClazz) {
if (clazz == null || metaClazz == null) return false;
return clazz.metadata.firstWhere((ElementAnnotation meta) {
// TODO(kegluneq): Make this recognize non-ConstructorElement annotations.
return meta.element is ConstructorElement &&
isAnnotationMatch(meta.element.returnType, metaClazz);
}, orElse: () => null) != null;
}

View File

@ -1,83 +0,0 @@
library angular2.transform;
import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/element.dart';
import 'annotation_processor.dart';
import 'common/logging.dart';
import 'resolvers.dart';
/// Walks through an Angular2 application, finding all classes matching the
/// provided [annotationMatcher].
class AngularVisibleTraversal {
final Angular2Types _types;
final _ComponentParsingAstVisitor _visitor;
AngularVisibleTraversal(this._types, AnnotationMatcher annotationMatcher)
: _visitor = new _ComponentParsingAstVisitor(annotationMatcher);
/// Walks an Angular2 application, starting with the class represented by
/// [entryPoint], which must be annotated as an Angular2 [Component].
///
/// We recursively check the entryPoint's annotations and constructor
/// arguments for types which match the provided [annotationMatcher].
void traverse(ClassElement entryPoint) {
if (!_types.isComponent(entryPoint)) {
throw new ArgumentError.value(entryPoint, 'entryPoint',
'Provided entryPoint must be annotated as a Component');
}
entryPoint.node.accept(_visitor);
}
}
class _ComponentParsingAstVisitor extends Object
with RecursiveAstVisitor<Object> {
final Set<ClassElement> _seen = new Set();
final AnnotationMatcher _matcher;
_ComponentParsingAstVisitor(this._matcher);
@override
Object visitClassDeclaration(ClassDeclaration node) {
if (node.element != null) {
if (_seen.contains(node.element)) return null;
_seen.add(node.element);
}
// Process the class itself.
node.name.accept(this);
// Process metadata information, ignoring [FieldDeclaration]s and
// [MethodDeclaration]s (see below).
node.metadata.forEach((Annotation meta) => meta.accept(this));
// Process constructor parameters, fields & methods are ignored below.
node.members.forEach((m) => m.accept(this));
return null;
}
@override
Object visitFieldDeclaration(FieldDeclaration node) => null;
@override
Object visitMethodDeclaration(MethodDeclaration node) => null;
@override
Object visitAnnotation(Annotation node) {
// TODO(kegluneq): Visit only Angular2 annotations & subtypes.
return super.visitAnnotation(node);
}
@override
Object visitSimpleIdentifier(SimpleIdentifier node) {
if (node.bestElement != null) {
if (node.bestElement is ClassElement) {
var matches = _matcher.processAnnotations(node.bestElement);
// If any of these types are matches, recurse on them.
matches.forEach((match) => match.node.accept(this));
}
}
return super.visitSimpleIdentifier(node);
}
}

View File

@ -2,12 +2,11 @@ library angular2.transform.reflection_remover.ast_tester;
import 'package:analyzer/src/generated/ast.dart'; import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/element.dart'; import 'package:analyzer/src/generated/element.dart';
import 'package:angular2/src/transform/common/names.dart';
/// An object that checks for [ReflectionCapabilities] syntactically, that is, /// An object that checks for [ReflectionCapabilities] syntactically, that is,
/// without resolution information. /// without resolution information.
class AstTester { class AstTester {
static const REFLECTION_CAPABILITIES_NAME = 'ReflectionCapabilities';
const AstTester(); const AstTester();
bool isNewReflectionCapabilities(InstanceCreationExpression node) => bool isNewReflectionCapabilities(InstanceCreationExpression node) =>

View File

@ -1,8 +1,6 @@
library angular2.transform.reflection_remover.codegen; library angular2.transform.reflection_remover.codegen;
import 'package:analyzer/src/generated/ast.dart'; import 'package:analyzer/src/generated/ast.dart';
import 'package:barback/barback.dart';
import 'package:code_transformers/resolver.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import 'package:angular2/src/transform/common/names.dart'; import 'package:angular2/src/transform/common/names.dart';
@ -23,20 +21,6 @@ class Codegen {
if (this.prefix.isEmpty) throw new ArgumentError.value('(empty)', 'prefix'); if (this.prefix.isEmpty) throw new ArgumentError.value('(empty)', 'prefix');
} }
factory Codegen.fromResolver(
Resolver resolver, AssetId reflectionEntryPoint, AssetId newEntryPoint) {
var lib = resolver.getLibrary(reflectionEntryPoint);
var prefix = _PREFIX_BASE;
var idx = 0;
while (lib.imports.any((import) {
return import.prefix != null && import.prefix == prefix;
})) {
prefix = '${_PREFIX_BASE}${idx++}';
}
return new Codegen(reflectionEntryPoint, newEntryPoint, prefix: prefix);
}
/// Generates code to import the library containing the method which sets up /// Generates code to import the library containing the method which sets up
/// Angular2 reflection statically. /// Angular2 reflection statically.
/// ///

View File

@ -2,6 +2,7 @@ library angular2.transform.reflection_remover.rewriter;
import 'package:analyzer/src/generated/ast.dart'; import 'package:analyzer/src/generated/ast.dart';
import 'package:angular2/src/transform/common/logging.dart'; import 'package:angular2/src/transform/common/logging.dart';
import 'package:angular2/src/transform/common/names.dart';
import 'ast_tester.dart'; import 'ast_tester.dart';
import 'codegen.dart'; import 'codegen.dart';
@ -27,12 +28,11 @@ class Rewriter {
var visitor = new _FindReflectionCapabilitiesVisitor(_tester); var visitor = new _FindReflectionCapabilitiesVisitor(_tester);
node.accept(visitor); node.accept(visitor);
if (visitor.reflectionCapabilityImports.isEmpty) { if (visitor.reflectionCapabilityImports.isEmpty) {
logger.error( logger.error('Failed to find ${REFLECTION_CAPABILITIES_NAME} import.');
'Failed to find ${AstTester.REFLECTION_CAPABILITIES_NAME} import.');
return _code; return _code;
} }
if (visitor.reflectionCapabilityAssignments.isEmpty) { if (visitor.reflectionCapabilityAssignments.isEmpty) {
logger.error('Failed to find ${AstTester.REFLECTION_CAPABILITIES_NAME} ' logger.error('Failed to find ${REFLECTION_CAPABILITIES_NAME} '
'instantiation.'); 'instantiation.');
return _code; return _code;
} }
@ -120,7 +120,7 @@ class _FindReflectionCapabilitiesVisitor extends Object
if (_tester.isNewReflectionCapabilities(node) && if (_tester.isNewReflectionCapabilities(node) &&
!reflectionCapabilityAssignments.contains(node.parent)) { !reflectionCapabilityAssignments.contains(node.parent)) {
logger.error('Unexpected format in creation of ' logger.error('Unexpected format in creation of '
'${reflectionCapabilitiesTypeName}'); '${REFLECTION_CAPABILITIES_NAME}');
} }
return super.visitInstanceCreationExpression(node); return super.visitInstanceCreationExpression(node);
} }

View File

@ -9,7 +9,6 @@ import 'bind_generator/transformer.dart';
import 'reflection_remover/transformer.dart'; import 'reflection_remover/transformer.dart';
import 'template_compiler/transformer.dart'; import 'template_compiler/transformer.dart';
import 'common/formatter.dart' as formatter; import 'common/formatter.dart' as formatter;
import 'common/names.dart';
import 'common/options.dart'; import 'common/options.dart';
import 'common/options_reader.dart'; import 'common/options_reader.dart';

View File

@ -1,14 +1,9 @@
library angular2.test.transform.bind_generator.all_tests; library angular2.test.transform.bind_generator.all_tests;
import 'dart:io';
import 'package:barback/barback.dart'; import 'package:barback/barback.dart';
import 'package:angular2/src/transform/bind_generator/generator.dart'; import 'package:angular2/src/transform/bind_generator/generator.dart';
import 'package:angular2/src/transform/common/formatter.dart';
import 'package:code_transformers/tests.dart';
import 'package:dart_style/dart_style.dart'; import 'package:dart_style/dart_style.dart';
import 'package:path/path.dart' as path;
import 'package:guinness/guinness.dart'; import 'package:guinness/guinness.dart';
import 'package:unittest/vm_config.dart';
import '../common/read_file.dart'; import '../common/read_file.dart';

View File

@ -1,6 +1,7 @@
library angular2.test.transform.common.read_file; library angular2.test.transform.common.read_file;
import 'dart:async'; import 'dart:async';
import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:angular2/src/transform/common/asset_reader.dart'; import 'package:angular2/src/transform/common/asset_reader.dart';

View File

@ -1,14 +1,9 @@
library angular2.test.transform.directive_linker.all_tests; library angular2.test.transform.directive_linker.all_tests;
import 'package:barback/barback.dart'; import 'package:barback/barback.dart';
import 'package:angular2/src/transform/common/logging.dart' hide init;
import 'package:angular2/src/transform/common/formatter.dart' hide init;
import 'package:angular2/src/transform/directive_linker/linker.dart'; import 'package:angular2/src/transform/directive_linker/linker.dart';
import 'package:code_transformers/tests.dart';
import 'package:dart_style/dart_style.dart'; import 'package:dart_style/dart_style.dart';
import 'package:path/path.dart' as path;
import 'package:guinness/guinness.dart'; import 'package:guinness/guinness.dart';
import 'package:unittest/vm_config.dart';
import '../common/read_file.dart'; import '../common/read_file.dart';

View File

@ -1,14 +1,8 @@
library angular2.test.transform.directive_processor.all_tests; library angular2.test.transform.directive_processor.all_tests;
import 'dart:io';
import 'package:barback/barback.dart';
import 'package:angular2/src/transform/directive_processor/rewriter.dart'; import 'package:angular2/src/transform/directive_processor/rewriter.dart';
import 'package:angular2/src/transform/common/formatter.dart';
import 'package:code_transformers/tests.dart';
import 'package:dart_style/dart_style.dart'; import 'package:dart_style/dart_style.dart';
import 'package:path/path.dart' as path;
import 'package:guinness/guinness.dart'; import 'package:guinness/guinness.dart';
import 'package:unittest/vm_config.dart';
import '../common/read_file.dart'; import '../common/read_file.dart';

View File

@ -1,6 +1,5 @@
library angular2.test.transform.integration; library angular2.test.transform.integration;
import 'dart:io';
import 'package:angular2/src/dom/html_adapter.dart'; import 'package:angular2/src/dom/html_adapter.dart';
import 'package:angular2/src/transform/common/names.dart'; import 'package:angular2/src/transform/common/names.dart';
import 'package:angular2/transformer.dart'; import 'package:angular2/transformer.dart';

View File

@ -1,10 +1,8 @@
library angular2.test.transform.directive_processor.all_tests; library angular2.test.transform.template_compiler.all_tests;
import 'package:barback/barback.dart'; import 'package:barback/barback.dart';
import 'package:angular2/src/dom/html_adapter.dart'; import 'package:angular2/src/dom/html_adapter.dart';
import 'package:angular2/src/transform/common/asset_reader.dart'; import 'package:angular2/src/transform/common/asset_reader.dart';
import 'package:angular2/src/transform/common/logging.dart';
import 'package:angular2/src/transform/common/formatter.dart';
import 'package:angular2/src/transform/template_compiler/generator.dart'; import 'package:angular2/src/transform/template_compiler/generator.dart';
import 'package:dart_style/dart_style.dart'; import 'package:dart_style/dart_style.dart';
import 'package:guinness/guinness.dart'; import 'package:guinness/guinness.dart';