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:
parent
aba61f22a6
commit
7cac7c5157
|
@ -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
|
||||||
|
|
|
@ -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';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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';
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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) =>
|
||||||
|
|
|
@ -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.
|
||||||
///
|
///
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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';
|
||||||
|
|
||||||
|
|
|
@ -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';
|
||||||
|
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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';
|
||||||
|
|
||||||
|
|
|
@ -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';
|
||||||
|
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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';
|
||||||
|
|
Loading…
Reference in New Issue