perf(dart/transform) Restructure transform to independent phases
Update summary: - Removes the need for resolution, gaining transform speed at the cost of some precision and ability to detect errors - Generates type registrations in the package alongside their declarations - Ensures that line numbers do not change in transformed user code
This commit is contained in:
parent
08bd3a4443
commit
d0aceef4e0
92
modules/angular2/src/transform/bind_generator/generator.dart
Normal file
92
modules/angular2/src/transform/bind_generator/generator.dart
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
library angular2.src.transform.bind_generator.generator;
|
||||||
|
|
||||||
|
import 'package:analyzer/analyzer.dart';
|
||||||
|
import 'package:analyzer/src/generated/java_core.dart';
|
||||||
|
import 'package:angular2/src/transform/common/logging.dart';
|
||||||
|
import 'package:angular2/src/transform/common/names.dart';
|
||||||
|
import 'package:angular2/src/transform/common/visitor_mixin.dart';
|
||||||
|
|
||||||
|
String createNgSetters(String code, String path) {
|
||||||
|
if (_noSettersPresent(code)) return code;
|
||||||
|
|
||||||
|
var writer = new PrintStringWriter();
|
||||||
|
parseCompilationUnit(
|
||||||
|
code, name: path).accept(new CreateNgSettersVisitor(writer));
|
||||||
|
return writer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _noSettersPresent(String code) => code.indexOf('bind') < 0;
|
||||||
|
|
||||||
|
class CreateNgSettersVisitor extends ToSourceVisitor with VisitorMixin {
|
||||||
|
final PrintWriter writer;
|
||||||
|
final _ExtractSettersVisitor extractVisitor = new _ExtractSettersVisitor();
|
||||||
|
|
||||||
|
CreateNgSettersVisitor(PrintWriter writer)
|
||||||
|
: this.writer = writer,
|
||||||
|
super(writer);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object visitMethodInvocation(MethodInvocation node) {
|
||||||
|
var isRegisterType = node.methodName.toString() == REGISTER_TYPE_METHOD_NAME;
|
||||||
|
// The first argument to a `registerType` call is the type.
|
||||||
|
extractVisitor.currentName = node.argumentList.arguments[0] is Identifier
|
||||||
|
? node.argumentList.arguments[0]
|
||||||
|
: null;
|
||||||
|
extractVisitor.bindPieces.clear();
|
||||||
|
|
||||||
|
var retVal = super.visitMethodInvocation(node);
|
||||||
|
if (isRegisterType) {
|
||||||
|
if (extractVisitor.bindPieces.isNotEmpty) {
|
||||||
|
writer.print('..${REGISTER_SETTERS_METHOD_NAME}({');
|
||||||
|
writer.print(extractVisitor.bindPieces.join(','));
|
||||||
|
writer.print('})');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object visitMapLiteralEntry(MapLiteralEntry node) {
|
||||||
|
if (node.key is StringLiteral &&
|
||||||
|
stringLiteralToString(node.key) == 'annotations') {
|
||||||
|
node.value.accept(extractVisitor);
|
||||||
|
}
|
||||||
|
return super.visitMapLiteralEntry(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Visitor responsible for crawling the "annotations" value in a
|
||||||
|
/// `registerType` call and generating setters from any "bind" values found.
|
||||||
|
class _ExtractSettersVisitor extends Object
|
||||||
|
with RecursiveAstVisitor<Object>, VisitorMixin {
|
||||||
|
final List<String> bindPieces = [];
|
||||||
|
Identifier currentName = null;
|
||||||
|
|
||||||
|
void _extractFromMapLiteral(MapLiteral map) {
|
||||||
|
if (currentName == null) {
|
||||||
|
logger.error('Unexpected code path: `currentName` should never be null');
|
||||||
|
}
|
||||||
|
map.entries.forEach((entry) {
|
||||||
|
// TODO(kegluneq): Remove this restriction
|
||||||
|
if (entry.key is SimpleStringLiteral) {
|
||||||
|
var propName = entry.key.value;
|
||||||
|
bindPieces.add('"${propName}": ('
|
||||||
|
'${currentName} o, String value) => o.${propName} = value');
|
||||||
|
} else {
|
||||||
|
logger.error('`bind` currently only supports string literals');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object visitNamedExpression(NamedExpression node) {
|
||||||
|
if (node.name.label.toString() == 'bind') {
|
||||||
|
// TODO(kegluneq): Remove this restriction.
|
||||||
|
if (node.expression is MapLiteral) {
|
||||||
|
_extractFromMapLiteral(node.expression);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return super.visitNamedExpression(node);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
library angular2.src.transform.bind_generator.transformer;
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'package:angular2/src/transform/common/formatter.dart';
|
||||||
|
import 'package:angular2/src/transform/common/logging.dart' as log;
|
||||||
|
import 'package:angular2/src/transform/common/names.dart';
|
||||||
|
import 'package:angular2/src/transform/common/options.dart';
|
||||||
|
import 'package:barback/barback.dart';
|
||||||
|
|
||||||
|
import 'generator.dart';
|
||||||
|
|
||||||
|
/// Transformer responsible for reading .ngDeps.dart files and generating
|
||||||
|
/// setters from the "annotations" information in the generated
|
||||||
|
/// `registerType` calls.
|
||||||
|
///
|
||||||
|
/// These setters are registered in the same `setupReflection` function with
|
||||||
|
/// the `registerType` calls.
|
||||||
|
class BindGenerator extends Transformer {
|
||||||
|
final TransformerOptions options;
|
||||||
|
|
||||||
|
BindGenerator(this.options);
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool isPrimary(AssetId id) => id.path.endsWith(DEPS_EXTENSION);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future apply(Transform transform) async {
|
||||||
|
log.init(transform);
|
||||||
|
|
||||||
|
try {
|
||||||
|
var assetCode = await transform.primaryInput.readAsString();
|
||||||
|
var assetPath = transform.primaryInput.id.path;
|
||||||
|
var transformedCode = createNgSetters(assetCode, assetPath);
|
||||||
|
transform.addOutput(new Asset.fromString(transform.primaryInput.id,
|
||||||
|
formatter.format(transformedCode, uri: assetPath)));
|
||||||
|
} catch (ex, stackTrace) {
|
||||||
|
log.logger.error('Creating ng setters failed.\n'
|
||||||
|
'Exception: $ex\n'
|
||||||
|
'Stack Trace: $stackTrace');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
modules/angular2/src/transform/common/formatter.dart
Normal file
22
modules/angular2/src/transform/common/formatter.dart
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
library angular2.src.transform.common.formatter;
|
||||||
|
|
||||||
|
import 'package:dart_style/dart_style.dart';
|
||||||
|
|
||||||
|
import 'logging.dart';
|
||||||
|
|
||||||
|
DartFormatter _formatter;
|
||||||
|
|
||||||
|
void init(DartFormatter formatter) {
|
||||||
|
if (_formatter != null) {
|
||||||
|
logger.warning('Formatter is being overwritten.');
|
||||||
|
}
|
||||||
|
_formatter = formatter;
|
||||||
|
}
|
||||||
|
|
||||||
|
DartFormatter get formatter {
|
||||||
|
if (_formatter == null) {
|
||||||
|
logger.info('Formatter never initialized, using default formatter.');
|
||||||
|
_formatter = new DartFormatter();
|
||||||
|
}
|
||||||
|
return _formatter;
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
library angular2.src.transform.logging;
|
library angular2.src.transform.common.logging;
|
||||||
|
|
||||||
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';
|
||||||
@ -7,12 +7,7 @@ BuildLogger _logger;
|
|||||||
|
|
||||||
/// Prepares [logger] for use throughout the transformer.
|
/// Prepares [logger] for use throughout the transformer.
|
||||||
void init(Transform t) {
|
void init(Transform t) {
|
||||||
if (_logger == null) {
|
|
||||||
_logger = new BuildLogger(t);
|
_logger = new BuildLogger(t);
|
||||||
} else {
|
|
||||||
_logger.fine('Attempted to initialize logger multiple times.',
|
|
||||||
asset: t.primaryInput.id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The logger the transformer should use for messaging.
|
/// The logger the transformer should use for messaging.
|
7
modules/angular2/src/transform/common/names.dart
Normal file
7
modules/angular2/src/transform/common/names.dart
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
library angular2.src.transform.common.names;
|
||||||
|
|
||||||
|
const SETUP_METHOD_NAME = 'setupReflection';
|
||||||
|
const REFLECTOR_VAR_NAME = 'reflector';
|
||||||
|
const DEPS_EXTENSION = '.ngDeps.dart';
|
||||||
|
const REGISTER_TYPE_METHOD_NAME = 'registerType';
|
||||||
|
const REGISTER_SETTERS_METHOD_NAME = 'registerSetters';
|
35
modules/angular2/src/transform/common/ngdata.dart
Normal file
35
modules/angular2/src/transform/common/ngdata.dart
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
library angular2.src.transform.common.ng_data;
|
||||||
|
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
const NG_DATA_VERSION = 1;
|
||||||
|
|
||||||
|
class NgData extends Object {
|
||||||
|
int importOffset = 0;
|
||||||
|
int registerOffset = 0;
|
||||||
|
List<String> imports = [];
|
||||||
|
|
||||||
|
NgData();
|
||||||
|
|
||||||
|
factory NgData.fromJson(String json) {
|
||||||
|
var data = JSON.decode(json);
|
||||||
|
return new NgData()
|
||||||
|
..importOffset = data['importOffset']
|
||||||
|
..registerOffset = data['registerOffset']
|
||||||
|
..imports = data['imports'];
|
||||||
|
}
|
||||||
|
|
||||||
|
String toJson() {
|
||||||
|
return JSON.encode({
|
||||||
|
'version': NG_DATA_VERSION,
|
||||||
|
'importOffset': importOffset,
|
||||||
|
'registerOffset': registerOffset,
|
||||||
|
'imports': imports
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return '[NgData: ${toJson()}]';
|
||||||
|
}
|
||||||
|
}
|
29
modules/angular2/src/transform/common/options.dart
Normal file
29
modules/angular2/src/transform/common/options.dart
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
library angular2.src.transform.common.options;
|
||||||
|
|
||||||
|
const ENTRY_POINT_PARAM = 'entry_point';
|
||||||
|
const REFLECTION_ENTRY_POINT_PARAM = 'reflection_entry_point';
|
||||||
|
|
||||||
|
/// Provides information necessary to transform an Angular2 app.
|
||||||
|
class TransformerOptions {
|
||||||
|
/// The path to the file where the application's call to [bootstrap] is.
|
||||||
|
// TODO(kegluneq): Allow multiple entry points.
|
||||||
|
final String entryPoint;
|
||||||
|
|
||||||
|
/// The reflection entry point, that is, the path to the file where the
|
||||||
|
/// application's [ReflectionCapabilities] are set.
|
||||||
|
final String reflectionEntryPoint;
|
||||||
|
|
||||||
|
TransformerOptions._internal(this.entryPoint, this.reflectionEntryPoint);
|
||||||
|
|
||||||
|
factory TransformerOptions(String entryPoint, {String reflectionEntryPoint}) {
|
||||||
|
if (entryPoint == null) {
|
||||||
|
throw new ArgumentError.notNull(ENTRY_POINT_PARAM);
|
||||||
|
} else if (entryPoint.isEmpty) {
|
||||||
|
throw new ArgumentError.value(entryPoint, 'entryPoint');
|
||||||
|
}
|
||||||
|
if (reflectionEntryPoint == null || entryPoint.isEmpty) {
|
||||||
|
reflectionEntryPoint = entryPoint;
|
||||||
|
}
|
||||||
|
return new TransformerOptions._internal(entryPoint, reflectionEntryPoint);
|
||||||
|
}
|
||||||
|
}
|
111
modules/angular2/src/transform/common/visitor_mixin.dart
Normal file
111
modules/angular2/src/transform/common/visitor_mixin.dart
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
library angular2.src.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
56
modules/angular2/src/transform/directive_linker/linker.dart
Normal file
56
modules/angular2/src/transform/directive_linker/linker.dart
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
library angular2.src.transform.directive_linker.linker;
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:angular2/src/transform/common/logging.dart';
|
||||||
|
import 'package:angular2/src/transform/common/names.dart';
|
||||||
|
import 'package:angular2/src/transform/common/ngdata.dart';
|
||||||
|
import 'package:barback/barback.dart';
|
||||||
|
import 'package:code_transformers/assets.dart';
|
||||||
|
import 'package:dart_style/dart_style.dart';
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
|
||||||
|
Future<String> linkNgDeps(Transform transform, String code, String path) async {
|
||||||
|
var commentIdx = code.lastIndexOf('//');
|
||||||
|
if (commentIdx < 0) return code;
|
||||||
|
|
||||||
|
var ngData = new NgData.fromJson(code.substring(commentIdx + 2));
|
||||||
|
|
||||||
|
StringBuffer importBuf =
|
||||||
|
new StringBuffer(code.substring(0, ngData.importOffset));
|
||||||
|
StringBuffer declarationBuf = new StringBuffer(
|
||||||
|
code.substring(ngData.importOffset, ngData.registerOffset));
|
||||||
|
String tail = code.substring(ngData.registerOffset, commentIdx);
|
||||||
|
|
||||||
|
var ngDeps = await _processNgImports(transform, ngData.imports);
|
||||||
|
|
||||||
|
for (var i = 0; i < ngDeps.length; ++i) {
|
||||||
|
importBuf.write('import \'${ngDeps[i]}\' as i${i};');
|
||||||
|
declarationBuf.write('i${i}.${SETUP_METHOD_NAME}(${REFLECTOR_VAR_NAME});');
|
||||||
|
}
|
||||||
|
|
||||||
|
return '${importBuf}${declarationBuf}${tail}';
|
||||||
|
}
|
||||||
|
|
||||||
|
String _toDepsUri(String importUri) =>
|
||||||
|
'${path.withoutExtension(importUri)}${DEPS_EXTENSION}';
|
||||||
|
|
||||||
|
bool _isNotDartImport(String importUri) {
|
||||||
|
return !importUri.startsWith('dart:');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<String>> _processNgImports(
|
||||||
|
Transform transform, List<String> imports) async {
|
||||||
|
var retVal = <String>[];
|
||||||
|
|
||||||
|
return Future
|
||||||
|
.wait(imports.where(_isNotDartImport).map(_toDepsUri).map((ngDepsUri) {
|
||||||
|
var importAsset = uriToAssetId(
|
||||||
|
transform.primaryInput.id, ngDepsUri, logger, null /* span */);
|
||||||
|
return transform.hasInput(importAsset).then((hasInput) {
|
||||||
|
if (hasInput) {
|
||||||
|
retVal.add(ngDepsUri);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})).then((_) => retVal);
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
library angular2.src.transform.directive_linker.transformer;
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:angular2/src/transform/common/formatter.dart';
|
||||||
|
import 'package:angular2/src/transform/common/logging.dart' as log;
|
||||||
|
import 'package:angular2/src/transform/common/names.dart';
|
||||||
|
import 'package:angular2/src/transform/common/options.dart';
|
||||||
|
import 'package:barback/barback.dart';
|
||||||
|
|
||||||
|
import 'linker.dart';
|
||||||
|
|
||||||
|
/// Transformer responsible for processing .ngDeps.dart files created by
|
||||||
|
/// [DirectiveProcessor] and ensuring that the generated calls to
|
||||||
|
/// `setupReflection` call the necessary `setupReflection` method in all
|
||||||
|
/// dependencies.
|
||||||
|
class DirectiveLinker extends Transformer {
|
||||||
|
final TransformerOptions options;
|
||||||
|
|
||||||
|
DirectiveLinker(this.options);
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool isPrimary(AssetId id) => id.path.endsWith(DEPS_EXTENSION);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future apply(Transform transform) async {
|
||||||
|
log.init(transform);
|
||||||
|
|
||||||
|
try {
|
||||||
|
var assetCode = await transform.primaryInput.readAsString();
|
||||||
|
var assetPath = transform.primaryInput.id.path;
|
||||||
|
var transformedCode = await linkNgDeps(transform, assetCode, assetPath);
|
||||||
|
var formattedCode = formatter.format(transformedCode, uri: assetPath);
|
||||||
|
transform.addOutput(
|
||||||
|
new Asset.fromString(transform.primaryInput.id, formattedCode));
|
||||||
|
} catch (ex, stackTrace) {
|
||||||
|
log.logger.error('Linking ng directives failed.\n'
|
||||||
|
'Exception: $ex\n'
|
||||||
|
'Stack Trace: $stackTrace');
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
207
modules/angular2/src/transform/directive_processor/rewriter.dart
Normal file
207
modules/angular2/src/transform/directive_processor/rewriter.dart
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
library angular2.src.transform.directive_processor;
|
||||||
|
|
||||||
|
import 'package:analyzer/analyzer.dart';
|
||||||
|
import 'package:analyzer/src/generated/java_core.dart';
|
||||||
|
import 'package:angular2/src/transform/common/logging.dart';
|
||||||
|
import 'package:angular2/src/transform/common/names.dart';
|
||||||
|
import 'package:angular2/src/transform/common/ngdata.dart';
|
||||||
|
import 'package:angular2/src/transform/common/visitor_mixin.dart';
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
|
||||||
|
import 'visitors.dart';
|
||||||
|
|
||||||
|
/// Generates a file registering all Angular 2 `Directive`s found in [code] in
|
||||||
|
/// ngDeps format [TODO(kegluneq): documentation reference needed]. [path] is
|
||||||
|
/// the path to the file (or asset) containing [code].
|
||||||
|
///
|
||||||
|
/// If no Angular 2 `Directive`s are found in [code], returns the empty
|
||||||
|
/// string unless [forceGenerate] is true, in which case an empty ngDeps
|
||||||
|
/// file is created.
|
||||||
|
String createNgDeps(String code, String path, {bool forceGenerate: false}) {
|
||||||
|
// TODO(kegluneq): Shortcut if we can determine that there are no
|
||||||
|
// [Directive]s present.
|
||||||
|
var writer = new PrintStringWriter();
|
||||||
|
var visitor = new CreateNgDepsVisitor(writer, path);
|
||||||
|
parseCompilationUnit(code, name: path).accept(visitor);
|
||||||
|
if (visitor.foundNgDirectives || forceGenerate) {
|
||||||
|
return writer.toString();
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Visitor responsible for processing [CompilationUnit] and creating an
|
||||||
|
/// associated .ngDeps.dart file.
|
||||||
|
class CreateNgDepsVisitor extends Object
|
||||||
|
with SimpleAstVisitor<Object>, VisitorMixin {
|
||||||
|
final PrintWriter writer;
|
||||||
|
final _Tester _tester = const _Tester();
|
||||||
|
bool foundNgDirectives = false;
|
||||||
|
bool wroteImport = false;
|
||||||
|
final ToSourceVisitor _copyVisitor;
|
||||||
|
final FactoryTransformVisitor _factoryVisitor;
|
||||||
|
final ParameterTransformVisitor _paramsVisitor;
|
||||||
|
final AnnotationsTransformVisitor _metaVisitor;
|
||||||
|
final NgData _ngData = new NgData();
|
||||||
|
|
||||||
|
/// The path to the file which we are parsing.
|
||||||
|
final String importPath;
|
||||||
|
|
||||||
|
CreateNgDepsVisitor(PrintWriter writer, this.importPath)
|
||||||
|
: writer = writer,
|
||||||
|
_copyVisitor = new ToSourceVisitor(writer),
|
||||||
|
_factoryVisitor = new FactoryTransformVisitor(writer),
|
||||||
|
_paramsVisitor = new ParameterTransformVisitor(writer),
|
||||||
|
_metaVisitor = new AnnotationsTransformVisitor(writer);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void visitCompilationUnit(CompilationUnit node) {
|
||||||
|
visitNodeListWithSeparator(node.directives, " ");
|
||||||
|
_openFunctionWrapper();
|
||||||
|
visitNodeListWithSeparator(node.declarations, " ");
|
||||||
|
_closeFunctionWrapper();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _writeImport() {
|
||||||
|
writer.print('import \'${path.basename(importPath)}\';');
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object visitImportDirective(ImportDirective node) {
|
||||||
|
if (!wroteImport) {
|
||||||
|
_writeImport();
|
||||||
|
wroteImport = true;
|
||||||
|
}
|
||||||
|
_ngData.imports.add(node.uri.stringValue);
|
||||||
|
return node.accept(_copyVisitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object visitExportDirective(ExportDirective node) {
|
||||||
|
_ngData.imports.add(node.uri.stringValue);
|
||||||
|
return node.accept(_copyVisitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _openFunctionWrapper() {
|
||||||
|
// TODO(kegluneq): Use a [PrintWriter] with a length getter.
|
||||||
|
_ngData.importOffset = writer.toString().length;
|
||||||
|
writer.print('bool _visited = false;'
|
||||||
|
'void ${SETUP_METHOD_NAME}(${REFLECTOR_VAR_NAME}) {'
|
||||||
|
'if (_visited) return; _visited = true;');
|
||||||
|
}
|
||||||
|
|
||||||
|
void _closeFunctionWrapper() {
|
||||||
|
if (foundNgDirectives) {
|
||||||
|
writer.print(';');
|
||||||
|
}
|
||||||
|
// TODO(kegluneq): Use a [PrintWriter] with a length getter.
|
||||||
|
_ngData.registerOffset = writer.toString().length;
|
||||||
|
writer.print('}');
|
||||||
|
writer.print('// ${_ngData.toJson()}');
|
||||||
|
}
|
||||||
|
|
||||||
|
ConstructorDeclaration _getCtor(ClassDeclaration node) {
|
||||||
|
int numCtorsFound = 0;
|
||||||
|
var ctor = null;
|
||||||
|
|
||||||
|
for (ClassMember classMember in node.members) {
|
||||||
|
if (classMember is ConstructorDeclaration) {
|
||||||
|
numCtorsFound++;
|
||||||
|
ConstructorDeclaration constructor = classMember;
|
||||||
|
|
||||||
|
// Use the unnnamed constructor if it is present.
|
||||||
|
// Otherwise, use the first encountered.
|
||||||
|
if (ctor == null) {
|
||||||
|
ctor = constructor;
|
||||||
|
} else if (constructor.name == null) {
|
||||||
|
ctor = constructor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (numCtorsFound > 1) {
|
||||||
|
var ctorName = ctor.name;
|
||||||
|
ctorName = ctorName == null
|
||||||
|
? 'the unnamed constructor'
|
||||||
|
: 'constructor "${ctorName}"';
|
||||||
|
logger.warning('Found ${numCtorsFound} ctors for class ${node.name},'
|
||||||
|
'Using ${ctorName}.');
|
||||||
|
}
|
||||||
|
return ctor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _generateEmptyFactory(String typeName) {
|
||||||
|
writer.print('() => new ${typeName}()');
|
||||||
|
}
|
||||||
|
|
||||||
|
void _generateEmptyParams() => writer.print('const []');
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object visitClassDeclaration(ClassDeclaration node) {
|
||||||
|
var shouldProcess = node.metadata.any(_tester._isDirective);
|
||||||
|
|
||||||
|
if (shouldProcess) {
|
||||||
|
var ctor = _getCtor(node);
|
||||||
|
|
||||||
|
if (!foundNgDirectives) {
|
||||||
|
// The receiver for cascaded calls.
|
||||||
|
writer.print(REFLECTOR_VAR_NAME);
|
||||||
|
foundNgDirectives = true;
|
||||||
|
}
|
||||||
|
writer.print('..registerType(');
|
||||||
|
visitNode(node.name);
|
||||||
|
writer.print(', {"factory": ');
|
||||||
|
if (ctor == null) {
|
||||||
|
_generateEmptyFactory(node.name.toString());
|
||||||
|
} else {
|
||||||
|
ctor.accept(_factoryVisitor);
|
||||||
|
}
|
||||||
|
writer.print(', "parameters": ');
|
||||||
|
if (ctor == null) {
|
||||||
|
_generateEmptyParams();
|
||||||
|
} else {
|
||||||
|
ctor.accept(_paramsVisitor);
|
||||||
|
}
|
||||||
|
writer.print(', "annotations": ');
|
||||||
|
node.accept(_metaVisitor);
|
||||||
|
writer.print('})');
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object _nodeToSource(AstNode node) {
|
||||||
|
if (node == null) return null;
|
||||||
|
return node.accept(_copyVisitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object visitLibraryDirective(LibraryDirective node) => _nodeToSource(node);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object visitPartOfDirective(PartOfDirective node) {
|
||||||
|
// TODO(kegluneq): Consider importing [node.libraryName].
|
||||||
|
logger.warning('[${importPath}]: '
|
||||||
|
'Found `part of` directive while generating ${DEPS_EXTENSION} file, '
|
||||||
|
'Transform may fail due to missing imports in generated file.');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object visitPrefixedIdentifier(PrefixedIdentifier node) =>
|
||||||
|
_nodeToSource(node);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object visitSimpleIdentifier(SimpleIdentifier node) => _nodeToSource(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
class _Tester {
|
||||||
|
const _Tester();
|
||||||
|
|
||||||
|
bool _isDirective(Annotation meta) {
|
||||||
|
var metaName = meta.name.toString();
|
||||||
|
return metaName == 'Component' ||
|
||||||
|
metaName == 'Decorator' ||
|
||||||
|
metaName == 'Template';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
library angular2.src.transform.directive_processor.transformer;
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:angular2/src/transform/common/formatter.dart';
|
||||||
|
import 'package:angular2/src/transform/common/logging.dart' as log;
|
||||||
|
import 'package:angular2/src/transform/common/names.dart';
|
||||||
|
import 'package:angular2/src/transform/common/options.dart';
|
||||||
|
import 'package:barback/barback.dart';
|
||||||
|
|
||||||
|
import 'rewriter.dart';
|
||||||
|
|
||||||
|
/// Transformer responsible for processing all .dart assets and creating
|
||||||
|
/// .ngDeps.dart files which register @Injectable annotated classes with the
|
||||||
|
/// reflector.
|
||||||
|
///
|
||||||
|
/// This will also create .ngDeps.dart files for classes annotated
|
||||||
|
/// with @Component, @Template, @Decorator, etc.
|
||||||
|
///
|
||||||
|
/// This transformer is the first phase in a two-phase transform. It should
|
||||||
|
/// be followed by [DirectiveLinker].
|
||||||
|
class DirectiveProcessor extends Transformer {
|
||||||
|
final TransformerOptions options;
|
||||||
|
|
||||||
|
DirectiveProcessor(this.options);
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool isPrimary(AssetId id) => id.extension.endsWith('dart');
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future apply(Transform transform) async {
|
||||||
|
log.init(transform);
|
||||||
|
|
||||||
|
try {
|
||||||
|
var assetCode = await transform.primaryInput.readAsString();
|
||||||
|
var ngDepsSrc = createNgDeps(assetCode, transform.primaryInput.id.path,
|
||||||
|
forceGenerate: transform.primaryInput.id.path == options.entryPoint);
|
||||||
|
if (ngDepsSrc != null && ngDepsSrc.isNotEmpty) {
|
||||||
|
var ngDepsAssetId =
|
||||||
|
transform.primaryInput.id.changeExtension(DEPS_EXTENSION);
|
||||||
|
var exists = await transform.hasInput(ngDepsAssetId);
|
||||||
|
if (exists) {
|
||||||
|
log.logger.error('Clobbering ${ngDepsAssetId}. '
|
||||||
|
'This probably will not end well');
|
||||||
|
}
|
||||||
|
transform.addOutput(new Asset.fromString(ngDepsAssetId, ngDepsSrc));
|
||||||
|
}
|
||||||
|
} catch (ex, stackTrace) {
|
||||||
|
log.logger.warning('Processing ng directives failed.\n'
|
||||||
|
'Exception: $ex\n'
|
||||||
|
'Stack Trace: $stackTrace');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
181
modules/angular2/src/transform/directive_processor/visitors.dart
Normal file
181
modules/angular2/src/transform/directive_processor/visitors.dart
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
library angular2.src.transform.directive_processor;
|
||||||
|
|
||||||
|
import 'package:analyzer/analyzer.dart';
|
||||||
|
import 'package:analyzer/src/generated/java_core.dart';
|
||||||
|
import 'package:angular2/src/transform/common/logging.dart';
|
||||||
|
import 'package:angular2/src/transform/common/visitor_mixin.dart';
|
||||||
|
|
||||||
|
/// SourceVisitor designed to accept [ConstructorDeclaration] nodes.
|
||||||
|
class _CtorTransformVisitor extends ToSourceVisitor with VisitorMixin {
|
||||||
|
bool _withParameterTypes = true;
|
||||||
|
bool _withParameterNames = true;
|
||||||
|
final PrintWriter writer;
|
||||||
|
|
||||||
|
/// Maps field names to their declared types. This is populated whenever
|
||||||
|
/// the listener visits a [ConstructorDeclaration] node.
|
||||||
|
final Map<String, TypeName> _fieldNameToType = {};
|
||||||
|
|
||||||
|
_CtorTransformVisitor(PrintWriter writer)
|
||||||
|
: this.writer = writer,
|
||||||
|
super(writer);
|
||||||
|
|
||||||
|
/// If [_withParameterTypes] is true, this method outputs [node]'s type. If
|
||||||
|
/// [_withParameterNames] is true, this method outputs [node]'s identifier.
|
||||||
|
Object _visitNormalFormalParameter(TypeName type, SimpleIdentifier name) {
|
||||||
|
if (_withParameterTypes) {
|
||||||
|
visitNodeWithSuffix(type, ' ');
|
||||||
|
}
|
||||||
|
if (_withParameterNames) {
|
||||||
|
visitNode(name);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _buildFieldMap(ConstructorDeclaration node) {
|
||||||
|
ClassDeclaration clazz =
|
||||||
|
node.getAncestor((node) => node is ClassDeclaration);
|
||||||
|
_fieldNameToType.clear();
|
||||||
|
clazz.members.where((member) => member is FieldDeclaration).forEach(
|
||||||
|
(FieldDeclaration field) {
|
||||||
|
var type = field.fields.type;
|
||||||
|
if (type != null) {
|
||||||
|
field.fields.variables.forEach((VariableDeclaration decl) {
|
||||||
|
_fieldNameToType[decl.name.toString()] = type;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object visitSimpleFormalParameter(SimpleFormalParameter node) {
|
||||||
|
return _visitNormalFormalParameter(node.type, node.identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object visitFieldFormalParameter(FieldFormalParameter node) {
|
||||||
|
if (node.parameters != null) {
|
||||||
|
logger.error('Parameters in ctor not supported '
|
||||||
|
'(${node.toSource()})');
|
||||||
|
}
|
||||||
|
var type = node.type;
|
||||||
|
if (type == null) {
|
||||||
|
type = _fieldNameToType[node.identifier.toString()];
|
||||||
|
}
|
||||||
|
return _visitNormalFormalParameter(type, node.identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
|
||||||
|
logger.error('Function typed formal parameters not supported '
|
||||||
|
'(${node.toSource()})');
|
||||||
|
return _visitNormalFormalParameter(null, node.identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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) : super(writer);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object visitConstructorDeclaration(ConstructorDeclaration node) {
|
||||||
|
_buildFieldMap(node);
|
||||||
|
_withParameterNames = false;
|
||||||
|
_withParameterTypes = true;
|
||||||
|
writer.print('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(', ');
|
||||||
|
}
|
||||||
|
// TODO(kegluneq): Include annotations on parameters.
|
||||||
|
writer.print('const [');
|
||||||
|
parameters[i].accept(this);
|
||||||
|
writer.print(']');
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ToSourceVisitor designed to print 'factory' values for Angular2's
|
||||||
|
/// [registerType] calls.
|
||||||
|
class FactoryTransformVisitor extends _CtorTransformVisitor {
|
||||||
|
FactoryTransformVisitor(PrintWriter writer) : super(writer);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object visitConstructorDeclaration(ConstructorDeclaration node) {
|
||||||
|
_buildFieldMap(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 ToSourceVisitor with VisitorMixin {
|
||||||
|
final PrintWriter writer;
|
||||||
|
AnnotationsTransformVisitor(PrintWriter writer)
|
||||||
|
: this.writer = writer,
|
||||||
|
super(writer);
|
||||||
|
|
||||||
|
@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);
|
||||||
|
visitNodeWithPrefix(".", node.constructorName);
|
||||||
|
visitNode(node.arguments);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -8,7 +8,7 @@ import 'package:dart_style/dart_style.dart';
|
|||||||
import 'package:path/path.dart' as path;
|
import 'package:path/path.dart' as path;
|
||||||
|
|
||||||
import 'annotation_processor.dart';
|
import 'annotation_processor.dart';
|
||||||
import 'logging.dart';
|
import 'common/logging.dart';
|
||||||
|
|
||||||
/// Base class that maintains codegen state.
|
/// Base class that maintains codegen state.
|
||||||
class Context {
|
class Context {
|
||||||
@ -72,7 +72,7 @@ String codegenEntryPoint(Context context, {AssetId newEntryPoint}) {
|
|||||||
if (newEntryPoint == null) {
|
if (newEntryPoint == null) {
|
||||||
throw new ArgumentError.notNull('newEntryPoint');
|
throw new ArgumentError.notNull('newEntryPoint');
|
||||||
}
|
}
|
||||||
// TODO(jakemac): copyright and library declaration
|
// TODO(kegluneq): copyright declaration
|
||||||
var outBuffer = new StringBuffer()
|
var outBuffer = new StringBuffer()
|
||||||
..write(_libraryDeclaration)
|
..write(_libraryDeclaration)
|
||||||
..write(_reflectorImport);
|
..write(_reflectorImport);
|
||||||
@ -195,11 +195,14 @@ class _DirectiveRegistryImpl implements DirectiveRegistry {
|
|||||||
_writer.print('..registerType(');
|
_writer.print('..registerType(');
|
||||||
_codegenClassTypeString(element);
|
_codegenClassTypeString(element);
|
||||||
_writer.print(', {"factory": ');
|
_writer.print(', {"factory": ');
|
||||||
_codegenFactoryProp(ctorNode, element);
|
_codegenClassTypeString(element);
|
||||||
|
_writer.print('.ngFactory');
|
||||||
_writer.print(', "parameters": ');
|
_writer.print(', "parameters": ');
|
||||||
_codegenParametersProp(ctorNode);
|
_codegenClassTypeString(element);
|
||||||
|
_writer.print('.ngParameters');
|
||||||
_writer.print(', "annotations": ');
|
_writer.print(', "annotations": ');
|
||||||
_codegenAnnotationsProp(entry.node);
|
_codegenClassTypeString(element);
|
||||||
|
_writer.print('.ngAnnotations');
|
||||||
_writer.print('})');
|
_writer.print('})');
|
||||||
}
|
}
|
||||||
|
|
@ -9,7 +9,7 @@ import 'package:dart_style/dart_style.dart';
|
|||||||
import 'package:path/path.dart' as path;
|
import 'package:path/path.dart' as path;
|
||||||
|
|
||||||
import 'codegen.dart';
|
import 'codegen.dart';
|
||||||
import 'logging.dart';
|
import 'common/logging.dart';
|
||||||
import 'resolvers.dart';
|
import 'resolvers.dart';
|
||||||
|
|
||||||
/// Finds all calls to the Angular2 [ReflectionCapabilities] constructor
|
/// Finds all calls to the Angular2 [ReflectionCapabilities] constructor
|
@ -4,7 +4,7 @@ import 'package:analyzer/src/generated/ast.dart';
|
|||||||
import 'package:analyzer/src/generated/element.dart';
|
import 'package:analyzer/src/generated/element.dart';
|
||||||
|
|
||||||
import 'annotation_processor.dart';
|
import 'annotation_processor.dart';
|
||||||
import 'logging.dart';
|
import 'common/logging.dart';
|
||||||
import 'resolvers.dart';
|
import 'resolvers.dart';
|
||||||
|
|
||||||
/// Walks through an Angular2 application, finding all classes matching the
|
/// Walks through an Angular2 application, finding all classes matching the
|
@ -1,41 +0,0 @@
|
|||||||
library angular2.src.transform;
|
|
||||||
|
|
||||||
const entryPointParam = 'entry_point';
|
|
||||||
const reflectionEntryPointParam = 'reflection_entry_point';
|
|
||||||
const newEntryPointParam = 'new_entry_point';
|
|
||||||
|
|
||||||
/// Provides information necessary to transform an Angular2 app.
|
|
||||||
class TransformerOptions {
|
|
||||||
/// The file where the application's call to [bootstrap] is.
|
|
||||||
// TODO(kegluneq): Allow multiple entry points.
|
|
||||||
final String entryPoint;
|
|
||||||
|
|
||||||
/// The reflection entry point, that is, where the
|
|
||||||
/// application's [ReflectionCapabilities] are set.
|
|
||||||
final String reflectionEntryPoint;
|
|
||||||
|
|
||||||
/// The path where we should generate code.
|
|
||||||
final String newEntryPoint;
|
|
||||||
|
|
||||||
TransformerOptions._internal(
|
|
||||||
this.entryPoint, this.reflectionEntryPoint, this.newEntryPoint);
|
|
||||||
|
|
||||||
factory TransformerOptions(String entryPoint,
|
|
||||||
{String reflectionEntryPoint, String newEntryPoint}) {
|
|
||||||
if (entryPoint == null || entryPoint.isEmpty) {
|
|
||||||
throw new ArgumentError.notNull(entryPointParam);
|
|
||||||
}
|
|
||||||
if (reflectionEntryPoint == null || entryPoint.isEmpty) {
|
|
||||||
reflectionEntryPoint = entryPoint;
|
|
||||||
}
|
|
||||||
if (newEntryPoint == null || newEntryPoint.isEmpty) {
|
|
||||||
newEntryPoint =
|
|
||||||
reflectionEntryPoint.replaceFirst('.dart', '.bootstrap.dart');
|
|
||||||
if (newEntryPoint == reflectionEntryPoint) {
|
|
||||||
newEntryPoint = 'bootstrap.${newEntryPoint}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new TransformerOptions._internal(
|
|
||||||
entryPoint, reflectionEntryPoint, newEntryPoint);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,36 @@
|
|||||||
|
library angular2.src.transform.reflection_remover.ast_tester;
|
||||||
|
|
||||||
|
import 'package:analyzer/src/generated/ast.dart';
|
||||||
|
import 'package:analyzer/src/generated/element.dart';
|
||||||
|
|
||||||
|
/// An object that checks for [ReflectionCapabilities] syntactically, that is,
|
||||||
|
/// without resolution information.
|
||||||
|
class AstTester {
|
||||||
|
static const REFLECTION_CAPABILITIES_NAME = 'ReflectionCapabilities';
|
||||||
|
|
||||||
|
const AstTester();
|
||||||
|
|
||||||
|
bool isNewReflectionCapabilities(InstanceCreationExpression node) =>
|
||||||
|
'${node.constructorName.type.name}' == REFLECTION_CAPABILITIES_NAME;
|
||||||
|
|
||||||
|
bool isReflectionCapabilitiesImport(ImportDirective node) {
|
||||||
|
return node.uri.stringValue.endsWith("reflection_capabilities.dart");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An object that checks for [ReflectionCapabilities] using a fully resolved
|
||||||
|
/// Ast.
|
||||||
|
class ResolvedTester implements AstTester {
|
||||||
|
final ClassElement _forbiddenClass;
|
||||||
|
|
||||||
|
ResolvedTester(this._forbiddenClass);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,89 @@
|
|||||||
|
library angular2.src.transform.reflection_remover.codegen;
|
||||||
|
|
||||||
|
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:angular2/src/transform/common/names.dart';
|
||||||
|
|
||||||
|
class Codegen {
|
||||||
|
static const _PREFIX_BASE = 'ngStaticInit';
|
||||||
|
|
||||||
|
/// The prefix used to import our generated file.
|
||||||
|
final String prefix;
|
||||||
|
/// The import uri
|
||||||
|
final String importUri;
|
||||||
|
|
||||||
|
Codegen(String reflectionEntryPointPath, String newEntryPointPath,
|
||||||
|
{String prefix})
|
||||||
|
: this.prefix = prefix == null ? _PREFIX_BASE : prefix,
|
||||||
|
importUri = path.relative(newEntryPointPath,
|
||||||
|
from: path.dirname(reflectionEntryPointPath)) {
|
||||||
|
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
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
/// If [reflectorAssignment] is provided, it is expected to be the node
|
||||||
|
/// representing the [ReflectionCapabilities] assignment, and we will
|
||||||
|
/// attempt to parse the access of [reflector] from it so that [reflector] is
|
||||||
|
/// properly prefixed if necessary.
|
||||||
|
String codegenSetupReflectionCall(
|
||||||
|
{AssignmentExpression reflectorAssignment}) {
|
||||||
|
var reflectorExpression = null;
|
||||||
|
if (reflectorAssignment != null) {
|
||||||
|
reflectorExpression = reflectorAssignment.accept(new _ReflectorVisitor());
|
||||||
|
}
|
||||||
|
if (reflectorExpression == null) {
|
||||||
|
reflectorExpression = 'reflector';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '${prefix}.${SETUP_METHOD_NAME}(${reflectorExpression});';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A visitor whose job it is to find the access of [reflector].
|
||||||
|
class _ReflectorVisitor extends Object with SimpleAstVisitor<Expression> {
|
||||||
|
@override
|
||||||
|
Expression visitAssignmentExpression(AssignmentExpression node) {
|
||||||
|
if (node == null || node.leftHandSide == null) return null;
|
||||||
|
return node.leftHandSide.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Expression visitPropertyAccess(PropertyAccess node) {
|
||||||
|
if (node == null || node.target == null) return;
|
||||||
|
return node.target;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Expression visitPrefixedIdentifier(PrefixedIdentifier node) {
|
||||||
|
if (node == null || node.prefix == null) return null;
|
||||||
|
return node.prefix;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
library angular2.src.transform.reflection_remover.remove_reflection_capabilities;
|
||||||
|
|
||||||
|
import 'package:analyzer/analyzer.dart';
|
||||||
|
|
||||||
|
import 'codegen.dart';
|
||||||
|
import 'rewriter.dart';
|
||||||
|
|
||||||
|
/// Finds the call to the Angular2 [ReflectionCapabilities] constructor
|
||||||
|
/// in [code] and replaces it with a call to `setupReflection` in
|
||||||
|
/// [newEntryPoint].
|
||||||
|
///
|
||||||
|
/// [reflectionEntryPointPath] is the path where [code] is defined and is
|
||||||
|
/// used to display parsing errors.
|
||||||
|
///
|
||||||
|
/// This only searches [code] not `part`s, `import`s, `export`s, etc.
|
||||||
|
String removeReflectionCapabilities(
|
||||||
|
String code, String reflectionEntryPointPath, String newEntryPointPath) {
|
||||||
|
var codegen = new Codegen(reflectionEntryPointPath, newEntryPointPath);
|
||||||
|
return new Rewriter(code, codegen)
|
||||||
|
.rewrite(parseCompilationUnit(code, name: reflectionEntryPointPath));
|
||||||
|
}
|
127
modules/angular2/src/transform/reflection_remover/rewriter.dart
Normal file
127
modules/angular2/src/transform/reflection_remover/rewriter.dart
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
library angular2.src.transform.reflection_remover.rewriter;
|
||||||
|
|
||||||
|
import 'package:analyzer/src/generated/ast.dart';
|
||||||
|
import 'package:angular2/src/transform/common/logging.dart';
|
||||||
|
|
||||||
|
import 'ast_tester.dart';
|
||||||
|
import 'codegen.dart';
|
||||||
|
|
||||||
|
class Rewriter {
|
||||||
|
final String _code;
|
||||||
|
final Codegen _codegen;
|
||||||
|
final AstTester _tester;
|
||||||
|
|
||||||
|
Rewriter(this._code, this._codegen, {AstTester tester})
|
||||||
|
: _tester = tester == null ? const AstTester() : tester;
|
||||||
|
|
||||||
|
/// Rewrites the provided code removing imports of the
|
||||||
|
/// [ReflectionCapabilities] library and instantiations of
|
||||||
|
/// [ReflectionCapabilities], as detected by the (potentially) provided
|
||||||
|
/// [AstTester].
|
||||||
|
///
|
||||||
|
/// To the extent possible, this method does not change line numbers or
|
||||||
|
/// offsets in the provided code to facilitate debugging via source maps.
|
||||||
|
String rewrite(CompilationUnit node) {
|
||||||
|
if (node == null) throw new ArgumentError.notNull('node');
|
||||||
|
|
||||||
|
var visitor = new _FindReflectionCapabilitiesVisitor(_tester);
|
||||||
|
node.accept(visitor);
|
||||||
|
if (visitor.reflectionCapabilityImports.isEmpty) {
|
||||||
|
logger.error(
|
||||||
|
'Failed to find ${AstTester.REFLECTION_CAPABILITIES_NAME} import.');
|
||||||
|
return _code;
|
||||||
|
}
|
||||||
|
if (visitor.reflectionCapabilityAssignments.isEmpty) {
|
||||||
|
logger.error('Failed to find ${AstTester.REFLECTION_CAPABILITIES_NAME} '
|
||||||
|
'instantiation.');
|
||||||
|
return _code;
|
||||||
|
}
|
||||||
|
|
||||||
|
var compare = (AstNode a, AstNode b) => b.offset - a.offset;
|
||||||
|
visitor.reflectionCapabilityImports.sort(compare);
|
||||||
|
visitor.reflectionCapabilityAssignments.sort(compare);
|
||||||
|
|
||||||
|
var importAdded = false;
|
||||||
|
var buf = new StringBuffer();
|
||||||
|
var idx = visitor.reflectionCapabilityImports.fold(0,
|
||||||
|
(int lastIdx, ImportDirective node) {
|
||||||
|
buf.write(_code.substring(lastIdx, node.offset));
|
||||||
|
if ('${node.prefix}' == _codegen.prefix) {
|
||||||
|
logger.warning(
|
||||||
|
'Found import prefix "${_codegen.prefix}" in source file.'
|
||||||
|
' Transform may not succeed.');
|
||||||
|
}
|
||||||
|
buf.write(_commentedNode(node));
|
||||||
|
if (!importAdded) {
|
||||||
|
buf.write(_codegen.codegenImport());
|
||||||
|
importAdded = true;
|
||||||
|
}
|
||||||
|
return node.end;
|
||||||
|
});
|
||||||
|
|
||||||
|
var setupAdded = false;
|
||||||
|
idx = visitor.reflectionCapabilityAssignments.fold(idx,
|
||||||
|
(int lastIdx, AssignmentExpression assignNode) {
|
||||||
|
var node = assignNode;
|
||||||
|
while (node.parent is ExpressionStatement) {
|
||||||
|
node = node.parent;
|
||||||
|
}
|
||||||
|
buf.write(_code.substring(lastIdx, node.offset));
|
||||||
|
buf.write(_commentedNode(node));
|
||||||
|
if (!setupAdded) {
|
||||||
|
buf.write(_codegen.codegenSetupReflectionCall(
|
||||||
|
reflectorAssignment: assignNode));
|
||||||
|
setupAdded = true;
|
||||||
|
}
|
||||||
|
return node.end;
|
||||||
|
});
|
||||||
|
if (idx < _code.length) buf.write(_code.substring(idx));
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
String _commentedNode(AstNode node) {
|
||||||
|
// TODO(kegluneq): Return commented code once we generate all needed code.
|
||||||
|
return _code.substring(node.offset, node.end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Visitor responsible for rewriting the Angular 2 code which instantiates
|
||||||
|
/// [ReflectionCapabilities] and removing its associated import.
|
||||||
|
///
|
||||||
|
/// This breaks our dependency on dart:mirrors, which enables smaller code
|
||||||
|
/// size and better performance.
|
||||||
|
class _FindReflectionCapabilitiesVisitor extends Object
|
||||||
|
with RecursiveAstVisitor<Object> {
|
||||||
|
final reflectionCapabilityImports = new List<ImportDirective>();
|
||||||
|
final reflectionCapabilityAssignments = new List<AssignmentExpression>();
|
||||||
|
final AstTester _tester;
|
||||||
|
|
||||||
|
_FindReflectionCapabilitiesVisitor(this._tester);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object visitImportDirective(ImportDirective node) {
|
||||||
|
if (_tester.isReflectionCapabilitiesImport(node)) {
|
||||||
|
reflectionCapabilityImports.add(node);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object visitAssignmentExpression(AssignmentExpression node) {
|
||||||
|
if (node.rightHandSide is InstanceCreationExpression &&
|
||||||
|
_tester.isNewReflectionCapabilities(node.rightHandSide)) {
|
||||||
|
reflectionCapabilityAssignments.add(node);
|
||||||
|
}
|
||||||
|
return super.visitAssignmentExpression(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object visitInstanceCreationExpression(InstanceCreationExpression node) {
|
||||||
|
if (_tester.isNewReflectionCapabilities(node) &&
|
||||||
|
!reflectionCapabilityAssignments.contains(node.parent)) {
|
||||||
|
logger.error('Unexpected format in creation of '
|
||||||
|
'${reflectionCapabilitiesTypeName}');
|
||||||
|
}
|
||||||
|
return super.visitInstanceCreationExpression(node);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
library angular2.src.transform.reflection_remover.transformer;
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'package:angular2/src/transform/common/logging.dart' as log;
|
||||||
|
import 'package:angular2/src/transform/common/names.dart';
|
||||||
|
import 'package:angular2/src/transform/common/options.dart';
|
||||||
|
import 'package:barback/barback.dart';
|
||||||
|
|
||||||
|
import 'remove_reflection_capabilities.dart';
|
||||||
|
|
||||||
|
/// Transformer responsible for removing the import and instantiation of
|
||||||
|
/// [ReflectionCapabilities].
|
||||||
|
///
|
||||||
|
/// The goal of this is to break the app's dependency on dart:mirrors.
|
||||||
|
///
|
||||||
|
/// This transformer assumes that [DirectiveProcessor] and [DirectiveLinker]
|
||||||
|
/// have already been run and that a .ngDeps.dart file has been generated for
|
||||||
|
/// [options.entryPoint]. The instantiation of [ReflectionCapabilities] is
|
||||||
|
/// replaced by calling `setupReflection` in that .ngDeps.dart file.
|
||||||
|
class ReflectionRemover extends Transformer {
|
||||||
|
final TransformerOptions options;
|
||||||
|
|
||||||
|
ReflectionRemover(this.options);
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool isPrimary(AssetId id) => options.reflectionEntryPoint == id.path;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future apply(Transform transform) async {
|
||||||
|
log.init(transform);
|
||||||
|
|
||||||
|
try {
|
||||||
|
var newEntryPoint = new AssetId(
|
||||||
|
transform.primaryInput.id.package, options.entryPoint)
|
||||||
|
.changeExtension(DEPS_EXTENSION);
|
||||||
|
|
||||||
|
var assetCode = await transform.primaryInput.readAsString();
|
||||||
|
transform.addOutput(new Asset.fromString(transform.primaryInput.id,
|
||||||
|
removeReflectionCapabilities(
|
||||||
|
assetCode, transform.primaryInput.id.path, newEntryPoint.path)));
|
||||||
|
} catch (ex, stackTrace) {
|
||||||
|
log.logger.error('Removing reflection failed.\n'
|
||||||
|
'Exception: $ex\n'
|
||||||
|
'Stack Trace: $stackTrace');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,134 +1,35 @@
|
|||||||
library angular2.src.transform;
|
library angular2.src.transform;
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'package:analyzer/src/generated/ast.dart';
|
|
||||||
import 'package:analyzer/src/generated/element.dart';
|
|
||||||
import 'package:barback/barback.dart';
|
import 'package:barback/barback.dart';
|
||||||
import 'package:code_transformers/resolver.dart';
|
import 'package:dart_style/dart_style.dart';
|
||||||
|
|
||||||
import 'annotation_processor.dart';
|
import 'directive_linker/transformer.dart';
|
||||||
import 'codegen.dart' as codegen;
|
import 'directive_processor/transformer.dart';
|
||||||
import 'find_bootstrap.dart';
|
import 'bind_generator/transformer.dart';
|
||||||
import 'find_reflection_capabilities.dart';
|
import 'reflection_remover/transformer.dart';
|
||||||
import 'logging.dart' as log;
|
import 'common/formatter.dart' as formatter;
|
||||||
import 'options.dart';
|
import 'common/options.dart';
|
||||||
import 'resolvers.dart';
|
|
||||||
import 'traversal.dart';
|
|
||||||
|
|
||||||
export 'options.dart';
|
export 'common/options.dart';
|
||||||
|
|
||||||
/// Removes the mirror-based initialization logic and replaces it with static
|
/// Removes the mirror-based initialization logic and replaces it with static
|
||||||
/// logic.
|
/// logic.
|
||||||
class AngularTransformer extends Transformer {
|
class AngularTransformerGroup extends TransformerGroup {
|
||||||
final Resolvers _resolvers;
|
AngularTransformerGroup(TransformerOptions options) : super([
|
||||||
final TransformerOptions options;
|
[new DirectiveProcessor(options)],
|
||||||
|
[new DirectiveLinker(options)],
|
||||||
|
[new BindGenerator(options), new ReflectionRemover(options)]
|
||||||
|
]) {
|
||||||
|
formatter.init(new DartFormatter());
|
||||||
|
}
|
||||||
|
|
||||||
AngularTransformer(this.options) : _resolvers = createResolvers();
|
factory AngularTransformerGroup.asPlugin(BarbackSettings settings) {
|
||||||
|
return new AngularTransformerGroup(_parseOptions(settings));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
factory AngularTransformer.asPlugin(BarbackSettings settings) {
|
TransformerOptions _parseOptions(BarbackSettings settings) {
|
||||||
var config = settings.configuration;
|
var config = settings.configuration;
|
||||||
return new AngularTransformer(new TransformerOptions(
|
return new TransformerOptions(config[ENTRY_POINT_PARAM],
|
||||||
config[entryPointParam],
|
reflectionEntryPoint: config[REFLECTION_ENTRY_POINT_PARAM]);
|
||||||
reflectionEntryPoint: config[reflectionEntryPointParam],
|
|
||||||
newEntryPoint: config[newEntryPointParam]));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isPrimary(AssetId id) => options.reflectionEntryPoint == id.path;
|
|
||||||
|
|
||||||
Future apply(Transform transform) {
|
|
||||||
log.init(transform);
|
|
||||||
|
|
||||||
var entryPointId =
|
|
||||||
new AssetId(transform.primaryInput.id.package, options.entryPoint);
|
|
||||||
var reflectionEntryPointId = new AssetId(
|
|
||||||
transform.primaryInput.id.package, options.reflectionEntryPoint);
|
|
||||||
var newEntryPointId =
|
|
||||||
new AssetId(transform.primaryInput.id.package, options.newEntryPoint);
|
|
||||||
|
|
||||||
var reflectionExists = transform.hasInput(reflectionEntryPointId);
|
|
||||||
var newEntryPointExists = transform.hasInput(newEntryPointId);
|
|
||||||
|
|
||||||
Resolver myResolver;
|
|
||||||
return Future
|
|
||||||
.wait([reflectionExists, newEntryPointExists])
|
|
||||||
.then((existsList) {
|
|
||||||
if (!existsList[0]) {
|
|
||||||
log.logger.error('Reflection entry point file '
|
|
||||||
'${reflectionEntryPointId} does not exist.');
|
|
||||||
} else if (existsList[1]) {
|
|
||||||
log.logger
|
|
||||||
.error('New entry point file $newEntryPointId already exists.');
|
|
||||||
} else {
|
|
||||||
return _resolvers
|
|
||||||
.get(transform, [entryPointId, reflectionEntryPointId])
|
|
||||||
.then((resolver) {
|
|
||||||
myResolver = resolver;
|
|
||||||
try {
|
|
||||||
String reflectionCapabilitiesCreation = findReflectionCapabilities(
|
|
||||||
resolver, reflectionEntryPointId, newEntryPointId);
|
|
||||||
|
|
||||||
transform.addOutput(new Asset.fromString(
|
|
||||||
reflectionEntryPointId, reflectionCapabilitiesCreation));
|
|
||||||
// Find the call to `new ReflectionCapabilities()`
|
|
||||||
// Generate new source.
|
|
||||||
} catch (err, stackTrace) {
|
|
||||||
log.logger.error('${err}: ${stackTrace}',
|
|
||||||
asset: reflectionEntryPointId);
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
new _BootstrapFileBuilder(
|
|
||||||
resolver, transform, entryPointId, newEntryPointId).run();
|
|
||||||
} catch (err, stackTrace) {
|
|
||||||
log.logger.error('${err}: ${stackTrace}',
|
|
||||||
asset: transform.primaryInput.id);
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}).whenComplete(() {
|
|
||||||
if (myResolver != null) {
|
|
||||||
myResolver.release();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _BootstrapFileBuilder {
|
|
||||||
final Resolver _resolver;
|
|
||||||
final Transform _transform;
|
|
||||||
final AssetId _entryPoint;
|
|
||||||
final AssetId _newEntryPoint;
|
|
||||||
|
|
||||||
_BootstrapFileBuilder(Resolver resolver, Transform transform,
|
|
||||||
this._entryPoint, this._newEntryPoint)
|
|
||||||
: _resolver = resolver,
|
|
||||||
_transform = transform;
|
|
||||||
|
|
||||||
/// Adds the new entry point file to the transform. Should only be ran once.
|
|
||||||
void run() {
|
|
||||||
Set<BootstrapCallInfo> bootstrapCalls =
|
|
||||||
findBootstrapCalls(_resolver, _resolver.getLibrary(_entryPoint));
|
|
||||||
|
|
||||||
log.logger.info('found ${bootstrapCalls.length} call(s) to `bootstrap`');
|
|
||||||
bootstrapCalls.forEach((BootstrapCallInfo info) {
|
|
||||||
log.logger.info('Arg1: ${info.bootstrapType}');
|
|
||||||
});
|
|
||||||
|
|
||||||
var types = new Angular2Types(_resolver);
|
|
||||||
// TODO(kegluneq): Also match [Inject].
|
|
||||||
var matcher = new AnnotationMatcher(
|
|
||||||
new Set.from([types.directiveAnnotation, types.templateAnnotation]));
|
|
||||||
|
|
||||||
var traversal = new AngularVisibleTraversal(types, matcher);
|
|
||||||
bootstrapCalls.forEach((call) => traversal.traverse(call.bootstrapType));
|
|
||||||
|
|
||||||
var context = new codegen.Context();
|
|
||||||
matcher.matchQueue
|
|
||||||
.forEach((entry) => context.directiveRegistry.register(entry));
|
|
||||||
|
|
||||||
_transform.addOutput(new Asset.fromString(_newEntryPoint,
|
|
||||||
codegen.codegenEntryPoint(context, newEntryPoint: _newEntryPoint)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
library bar;
|
||||||
|
|
||||||
|
import 'bar.dart';
|
||||||
|
import 'package:angular2/src/core/annotations/annotations.dart';
|
||||||
|
|
||||||
|
bool _visited = false;
|
||||||
|
void setupReflection(reflector) {
|
||||||
|
if (_visited) return;
|
||||||
|
_visited = true;
|
||||||
|
reflector
|
||||||
|
..registerType(MyComponent, {
|
||||||
|
"factory": () => new MyComponent(),
|
||||||
|
"parameters": const [],
|
||||||
|
"annotations": const [
|
||||||
|
const Component(selector: 'soup', componentServices: const [ToolTip])
|
||||||
|
]
|
||||||
|
})
|
||||||
|
..registerType(ToolTip, {
|
||||||
|
"factory": () => new ToolTip(),
|
||||||
|
"parameters": const [],
|
||||||
|
"annotations": const [
|
||||||
|
const Decorator(
|
||||||
|
selector: '[tool-tip]', bind: const {'text': 'tool-tip'})
|
||||||
|
]
|
||||||
|
})
|
||||||
|
..registerSetters({"text": (ToolTip o, String value) => o.text = value});
|
||||||
|
}
|
@ -1,26 +0,0 @@
|
|||||||
library angular2.src.transform.generated;
|
|
||||||
|
|
||||||
import 'package:angular2/src/reflection/reflection.dart' show reflector;
|
|
||||||
import 'bar.dart' as i0;
|
|
||||||
import 'package:angular2/src/core/annotations/annotations.dart' as i1;
|
|
||||||
|
|
||||||
setupReflection() {
|
|
||||||
reflector
|
|
||||||
..registerType(i0.MyComponent, {
|
|
||||||
"factory": () => new i0.MyComponent(),
|
|
||||||
"parameters": const [const []],
|
|
||||||
"annotations": const [
|
|
||||||
const i1.Component(
|
|
||||||
selector: 'soup', componentServices: const [i0.ToolTip])
|
|
||||||
]
|
|
||||||
})
|
|
||||||
..registerType(i0.ToolTip, {
|
|
||||||
"factory": () => new i0.ToolTip(),
|
|
||||||
"parameters": const [const []],
|
|
||||||
"annotations": const [
|
|
||||||
const i1.Decorator(
|
|
||||||
selector: '[tool-tip]', bind: const {'text': 'tool-tip'})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
..registerSetters({"text": (i0.ToolTip o, String value) => o.text = value});
|
|
||||||
}
|
|
10
modules/angular2/test/transform/chained_deps_files/bar.dart
Normal file
10
modules/angular2/test/transform/chained_deps_files/bar.dart
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
library bar;
|
||||||
|
|
||||||
|
import 'package:angular2/src/core/annotations/annotations.dart';
|
||||||
|
import 'foo.dart' as dep;
|
||||||
|
|
||||||
|
@Component(
|
||||||
|
selector: '[soup]', componentServices: const [dep.DependencyComponent])
|
||||||
|
class MyComponent {
|
||||||
|
MyComponent();
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
library bar;
|
||||||
|
|
||||||
|
import 'bar.dart';
|
||||||
|
import 'package:angular2/src/core/annotations/annotations.dart';
|
||||||
|
import 'foo.dart' as dep;
|
||||||
|
import 'foo.ngDeps.dart' as i0;
|
||||||
|
|
||||||
|
bool _visited = false;
|
||||||
|
void setupReflection(reflector) {
|
||||||
|
if (_visited) return;
|
||||||
|
_visited = true;
|
||||||
|
reflector
|
||||||
|
..registerType(MyComponent, {
|
||||||
|
"factory": () => new MyComponent(),
|
||||||
|
"parameters": const [],
|
||||||
|
"annotations": const [
|
||||||
|
const Component(
|
||||||
|
selector: '[soup]',
|
||||||
|
componentServices: const [dep.DependencyComponent])
|
||||||
|
]
|
||||||
|
});
|
||||||
|
i0.setupReflection(reflector);
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
library foo;
|
||||||
|
|
||||||
|
import 'foo.dart';
|
||||||
|
import 'package:angular2/src/core/annotations/annotations.dart';
|
||||||
|
|
||||||
|
bool _visited = false;
|
||||||
|
void setupReflection(reflector) {
|
||||||
|
if (_visited) return;
|
||||||
|
_visited = true;
|
||||||
|
reflector
|
||||||
|
..registerType(DependencyComponent, {
|
||||||
|
"factory": () => new DependencyComponent(),
|
||||||
|
"parameters": const [],
|
||||||
|
"annotations": const [const Component(selector: '[salad]')]
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
library web_foo;
|
||||||
|
|
||||||
|
import 'package:angular2/src/core/application.dart';
|
||||||
|
import 'package:angular2/src/reflection/reflection_capabilities.dart';
|
||||||
|
import 'bar.dart';
|
||||||
|
import 'a:web/bar.ngDeps.dart' as i0;
|
||||||
|
|
||||||
|
bool _visited = false;
|
||||||
|
void setupReflection(reflector) {
|
||||||
|
if (_visited) return;
|
||||||
|
_visited = true;
|
||||||
|
i0.setupReflection(reflector);
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
library foo;
|
||||||
|
|
||||||
|
import 'package:angular2/src/core/annotations/annotations.dart';
|
||||||
|
|
||||||
|
@Component(selector: '[salad]')
|
||||||
|
class DependencyComponent {
|
||||||
|
DependencyComponent();
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
library web_foo;
|
||||||
|
|
||||||
|
import 'package:angular2/src/core/application.dart';
|
||||||
|
import 'package:angular2/src/reflection/reflection_capabilities.dart';
|
||||||
|
import 'bar.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
reflector.reflectionCapabilities = new ReflectionCapabilities();
|
||||||
|
bootstrap(MyComponent);
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
library bar;
|
||||||
|
|
||||||
|
import 'bar.dart';
|
||||||
|
import 'package:angular2/src/core/annotations/annotations.dart';
|
||||||
|
import 'foo.dart';
|
||||||
|
|
||||||
|
bool _visited = false;
|
||||||
|
void setupReflection(reflector) {
|
||||||
|
if (_visited) return;
|
||||||
|
_visited = true;
|
||||||
|
reflector
|
||||||
|
..registerType(MyComponent, {
|
||||||
|
"factory": (MyContext c) => new MyComponent(c),
|
||||||
|
"parameters": const [const [MyContext]],
|
||||||
|
"annotations": const [
|
||||||
|
const Component(componentServices: const [MyContext])
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
@ -1,17 +0,0 @@
|
|||||||
library angular2.src.transform.generated;
|
|
||||||
|
|
||||||
import 'package:angular2/src/reflection/reflection.dart' show reflector;
|
|
||||||
import 'bar.dart' as i0;
|
|
||||||
import 'foo.dart' as i1;
|
|
||||||
import 'package:angular2/src/core/annotations/annotations.dart' as i2;
|
|
||||||
|
|
||||||
setupReflection() {
|
|
||||||
reflector
|
|
||||||
..registerType(i0.MyComponent, {
|
|
||||||
"factory": (i1.MyContext c) => new i0.MyComponent(c),
|
|
||||||
"parameters": const [const [i1.MyContext]],
|
|
||||||
"annotations": const [
|
|
||||||
const i2.Component(componentServices: const [i1.MyContext])
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
|
@ -0,0 +1,7 @@
|
|||||||
|
Tests that the reflection removal step:
|
||||||
|
1. Comments out the import of reflection_capabilities.dart
|
||||||
|
2. Comments out the instantiation of `ReflectionCapabilities`
|
||||||
|
3. Adds the appropriate import.
|
||||||
|
4. Adds the call to `setupReflection`
|
||||||
|
5. Does not change line numbers in the source.
|
||||||
|
6. Makes minimal changes to source offsets.
|
@ -0,0 +1,22 @@
|
|||||||
|
library angular2.test.transform.reflection_remover_files;
|
||||||
|
|
||||||
|
// This file is intentionally formatted as a string to avoid having the
|
||||||
|
// automatic transformer prettify it.
|
||||||
|
//
|
||||||
|
// This file represents transformed user code. Because this code will be
|
||||||
|
// linked to output by a source map, we cannot change line numbers from the
|
||||||
|
// original code and we therefore add our generated code on the same line as
|
||||||
|
// those we are removing.
|
||||||
|
|
||||||
|
var code = """
|
||||||
|
library web_foo;
|
||||||
|
|
||||||
|
import 'package:angular2/src/core/application.dart';
|
||||||
|
import 'package:angular2/src/reflection/reflection.dart';
|
||||||
|
import 'package:angular2/src/reflection/reflection_capabilities.dart';import 'index.ngDeps.dart' as ngStaticInit;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
reflector.reflectionCapabilities = new ReflectionCapabilities();ngStaticInit.setupReflection(reflector);
|
||||||
|
bootstrap(MyComponent);
|
||||||
|
}
|
||||||
|
""";
|
@ -0,0 +1,10 @@
|
|||||||
|
library web_foo;
|
||||||
|
|
||||||
|
import 'package:angular2/src/core/application.dart';
|
||||||
|
import 'package:angular2/src/reflection/reflection.dart';
|
||||||
|
import 'package:angular2/src/reflection/reflection_capabilities.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
reflector.reflectionCapabilities = new ReflectionCapabilities();
|
||||||
|
bootstrap(MyComponent);
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
library bar;
|
||||||
|
|
||||||
|
import 'bar.dart';
|
||||||
|
import 'package:angular2/src/core/annotations/annotations.dart';
|
||||||
|
|
||||||
|
bool _visited = false;
|
||||||
|
void setupReflection(reflector) {
|
||||||
|
if (_visited) return;
|
||||||
|
_visited = true;
|
||||||
|
reflector
|
||||||
|
..registerType(MyComponent, {
|
||||||
|
"factory": () => new MyComponent(),
|
||||||
|
"parameters": const [],
|
||||||
|
"annotations": const [const Component(selector: '[soup]')]
|
||||||
|
});
|
||||||
|
}
|
@ -1,14 +0,0 @@
|
|||||||
library angular2.src.transform.generated;
|
|
||||||
|
|
||||||
import 'package:angular2/src/reflection/reflection.dart' show reflector;
|
|
||||||
import 'bar.dart' as i0;
|
|
||||||
import 'package:angular2/src/core/annotations/annotations.dart' as i1;
|
|
||||||
|
|
||||||
setupReflection() {
|
|
||||||
reflector
|
|
||||||
..registerType(i0.MyComponent, {
|
|
||||||
"factory": () => new i0.MyComponent(),
|
|
||||||
"parameters": const [const []],
|
|
||||||
"annotations": const [const i1.Component(selector: '[soup]')]
|
|
||||||
});
|
|
||||||
}
|
|
@ -2,12 +2,12 @@ library web_foo;
|
|||||||
|
|
||||||
import 'package:angular2/src/core/application.dart';
|
import 'package:angular2/src/core/application.dart';
|
||||||
import 'package:angular2/src/reflection/reflection.dart';
|
import 'package:angular2/src/reflection/reflection.dart';
|
||||||
import 'index.bootstrap.dart' as ngStaticInit;
|
import 'index.ngDeps.dart' as ngStaticInit;
|
||||||
import 'package:angular2/src/reflection/reflection_capabilities.dart';
|
import 'package:angular2/src/reflection/reflection_capabilities.dart';
|
||||||
import 'bar.dart';
|
import 'bar.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
ngStaticInit.setupReflection();
|
ngStaticInit.setupReflection(reflector);
|
||||||
reflector.reflectionCapabilities = new ReflectionCapabilities();
|
reflector.reflectionCapabilities = new ReflectionCapabilities();
|
||||||
bootstrap(MyComponent);
|
bootstrap(MyComponent);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
library web_foo;
|
||||||
|
|
||||||
|
import 'index.dart';
|
||||||
|
import 'package:angular2/src/core/application.dart';
|
||||||
|
import 'package:angular2/src/reflection/reflection.dart';
|
||||||
|
import 'package:angular2/src/reflection/reflection_capabilities.dart';
|
||||||
|
import 'bar.dart';
|
||||||
|
import 'bar.ngDeps.dart' as i0;
|
||||||
|
|
||||||
|
bool _visited = false;
|
||||||
|
void setupReflection(reflector) {
|
||||||
|
if (_visited) return;
|
||||||
|
_visited = true;
|
||||||
|
i0.setupReflection(reflector);
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
library bar;
|
||||||
|
|
||||||
|
import 'bar.dart';
|
||||||
|
import 'package:angular2/src/core/annotations/annotations.dart';
|
||||||
|
|
||||||
|
bool _visited = false;
|
||||||
|
void setupReflection(reflector) {
|
||||||
|
if (_visited) return;
|
||||||
|
_visited = true;
|
||||||
|
reflector
|
||||||
|
..registerType(MyComponent, {
|
||||||
|
"factory": () => new MyComponent(),
|
||||||
|
"parameters": const [],
|
||||||
|
"annotations": const [const Component(selector: '[soup]')]
|
||||||
|
});
|
||||||
|
}
|
@ -1,14 +0,0 @@
|
|||||||
library angular2.src.transform.generated;
|
|
||||||
|
|
||||||
import 'package:angular2/src/reflection/reflection.dart' show reflector;
|
|
||||||
import 'bar.dart' as i0;
|
|
||||||
import 'package:angular2/src/core/annotations/annotations.dart' as i1;
|
|
||||||
|
|
||||||
setupReflection() {
|
|
||||||
reflector
|
|
||||||
..registerType(i0.MyComponent, {
|
|
||||||
"factory": () => new i0.MyComponent(),
|
|
||||||
"parameters": const [const []],
|
|
||||||
"annotations": const [const i1.Component(selector: '[soup]')]
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
library angular2.test;
|
library angular2.test.transform;
|
||||||
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:barback/barback.dart';
|
import 'package:barback/barback.dart';
|
||||||
@ -8,31 +8,30 @@ import 'package:dart_style/dart_style.dart';
|
|||||||
import 'package:unittest/unittest.dart';
|
import 'package:unittest/unittest.dart';
|
||||||
import 'package:unittest/vm_config.dart';
|
import 'package:unittest/vm_config.dart';
|
||||||
|
|
||||||
|
import 'reflection_remover_files/expected/index.dart'
|
||||||
|
as reflection_remover_output;
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
useVMConfiguration();
|
useVMConfiguration();
|
||||||
|
group('Integration tests:', _integrationTests);
|
||||||
// TODO(kegluneq): Add a test for generating multiple annotations.
|
|
||||||
|
|
||||||
group('Annotation tests:', _runTests);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var formatter = new DartFormatter();
|
var formatter = new DartFormatter();
|
||||||
var transform = new AngularTransformer(new TransformerOptions('web/index.dart',
|
var transform = new AngularTransformerGroup(new TransformerOptions(
|
||||||
reflectionEntryPoint: 'web/index.dart',
|
'web/index.dart', reflectionEntryPoint: 'web/index.dart'));
|
||||||
newEntryPoint: 'web/index.bootstrap.dart'));
|
|
||||||
|
|
||||||
class TestConfig {
|
class IntegrationTestConfig {
|
||||||
final String name;
|
final String name;
|
||||||
final Map<String, String> assetPathToInputPath;
|
final Map<String, String> assetPathToInputPath;
|
||||||
final Map<String, String> assetPathToExpectedOutputPath;
|
final Map<String, String> assetPathToExpectedOutputPath;
|
||||||
|
|
||||||
TestConfig(this.name,
|
IntegrationTestConfig(this.name,
|
||||||
{Map<String, String> inputs, Map<String, String> outputs})
|
{Map<String, String> inputs, Map<String, String> outputs})
|
||||||
: this.assetPathToInputPath = inputs,
|
: this.assetPathToInputPath = inputs,
|
||||||
this.assetPathToExpectedOutputPath = outputs;
|
this.assetPathToExpectedOutputPath = outputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _runTests() {
|
void _integrationTests() {
|
||||||
/*
|
/*
|
||||||
* Each test has its own directory for inputs & an `expected` directory for
|
* Each test has its own directory for inputs & an `expected` directory for
|
||||||
* expected outputs.
|
* expected outputs.
|
||||||
@ -43,52 +42,53 @@ void _runTests() {
|
|||||||
var commonInputs = {
|
var commonInputs = {
|
||||||
'angular2|lib/src/core/annotations/annotations.dart':
|
'angular2|lib/src/core/annotations/annotations.dart':
|
||||||
'../../lib/src/core/annotations/annotations.dart',
|
'../../lib/src/core/annotations/annotations.dart',
|
||||||
'angular2|lib/src/core/application.dart': 'common.dart',
|
'angular2|lib/src/core/application.dart': 'common/application.dart',
|
||||||
'angular2|lib/src/reflection/reflection_capabilities.dart':
|
'angular2|lib/src/reflection/reflection_capabilities.dart':
|
||||||
'reflection_capabilities.dart'
|
'common/reflection_capabilities.dart'
|
||||||
};
|
};
|
||||||
|
|
||||||
var tests = [
|
var tests = [
|
||||||
new TestConfig('Simple',
|
new IntegrationTestConfig('Simple',
|
||||||
inputs: {
|
inputs: {
|
||||||
'a|web/index.dart': 'simple_annotation_files/index.dart',
|
'a|web/index.dart': 'simple_annotation_files/index.dart',
|
||||||
'a|web/bar.dart': 'simple_annotation_files/bar.dart'
|
'a|web/bar.dart': 'simple_annotation_files/bar.dart'
|
||||||
},
|
},
|
||||||
outputs: {
|
outputs: {
|
||||||
'a|web/index.bootstrap.dart':
|
'a|web/bar.ngDeps.dart':
|
||||||
'simple_annotation_files/expected/index.bootstrap.dart',
|
'simple_annotation_files/expected/bar.ngDeps.dart',
|
||||||
'a|web/index.dart': 'simple_annotation_files/expected/index.dart',
|
'a|web/index.ngDeps.dart':
|
||||||
|
'simple_annotation_files/expected/index.ngDeps.dart'
|
||||||
}),
|
}),
|
||||||
new TestConfig('Two injected dependencies',
|
new IntegrationTestConfig('Reflection Remover',
|
||||||
|
inputs: {'a|web/index.dart': 'reflection_remover_files/index.dart'},
|
||||||
|
outputs: {'a|web/index.dart': reflection_remover_output.code}),
|
||||||
|
new IntegrationTestConfig('Two injected dependencies',
|
||||||
inputs: {
|
inputs: {
|
||||||
'a|web/index.dart': 'two_deps_files/index.dart',
|
'a|web/index.dart': 'two_deps_files/index.dart',
|
||||||
'a|web/foo.dart': 'two_deps_files/foo.dart',
|
'a|web/foo.dart': 'two_deps_files/foo.dart',
|
||||||
'a|web/bar.dart': 'two_deps_files/bar.dart'
|
'a|web/bar.dart': 'two_deps_files/bar.dart'
|
||||||
},
|
},
|
||||||
outputs: {
|
outputs: {
|
||||||
'a|web/index.bootstrap.dart':
|
'a|web/bar.ngDeps.dart': 'two_deps_files/expected/bar.ngDeps.dart'
|
||||||
'two_deps_files/expected/index.bootstrap.dart'
|
|
||||||
}),
|
}),
|
||||||
new TestConfig('List of types',
|
new IntegrationTestConfig('List of types',
|
||||||
inputs: {
|
inputs: {
|
||||||
'a|web/index.dart': 'list_of_types_files/index.dart',
|
'a|web/index.dart': 'list_of_types_files/index.dart',
|
||||||
'a|web/foo.dart': 'list_of_types_files/foo.dart',
|
'a|web/foo.dart': 'list_of_types_files/foo.dart',
|
||||||
'a|web/bar.dart': 'list_of_types_files/bar.dart'
|
'a|web/bar.dart': 'list_of_types_files/bar.dart'
|
||||||
},
|
},
|
||||||
outputs: {
|
outputs: {
|
||||||
'a|web/index.bootstrap.dart':
|
'a|web/bar.ngDeps.dart': 'list_of_types_files/expected/bar.ngDeps.dart'
|
||||||
'list_of_types_files/expected/index.bootstrap.dart'
|
|
||||||
}),
|
}),
|
||||||
new TestConfig('Component with synthetic Constructor',
|
new IntegrationTestConfig('Component with synthetic Constructor',
|
||||||
inputs: {
|
inputs: {
|
||||||
'a|web/index.dart': 'synthetic_ctor_files/index.dart',
|
'a|web/index.dart': 'synthetic_ctor_files/index.dart',
|
||||||
'a|web/bar.dart': 'synthetic_ctor_files/bar.dart'
|
'a|web/bar.dart': 'synthetic_ctor_files/bar.dart'
|
||||||
},
|
},
|
||||||
outputs: {
|
outputs: {
|
||||||
'a|web/index.bootstrap.dart':
|
'a|web/bar.ngDeps.dart': 'synthetic_ctor_files/expected/bar.ngDeps.dart'
|
||||||
'synthetic_ctor_files/expected/index.bootstrap.dart'
|
|
||||||
}),
|
}),
|
||||||
new TestConfig('Component with two annotations',
|
new IntegrationTestConfig('Component with two annotations',
|
||||||
inputs: {
|
inputs: {
|
||||||
'a|web/index.dart': 'two_annotations_files/index.dart',
|
'a|web/index.dart': 'two_annotations_files/index.dart',
|
||||||
'a|web/bar.dart': 'two_annotations_files/bar.dart',
|
'a|web/bar.dart': 'two_annotations_files/bar.dart',
|
||||||
@ -96,17 +96,25 @@ void _runTests() {
|
|||||||
'../../lib/src/core/annotations/template.dart'
|
'../../lib/src/core/annotations/template.dart'
|
||||||
},
|
},
|
||||||
outputs: {
|
outputs: {
|
||||||
'a|web/index.bootstrap.dart':
|
'a|web/bar.ngDeps.dart': 'two_annotations_files/expected/bar.ngDeps.dart'
|
||||||
'two_annotations_files/expected/index.bootstrap.dart'
|
|
||||||
}),
|
}),
|
||||||
new TestConfig('Basic `bind`',
|
new IntegrationTestConfig('Basic `bind`',
|
||||||
inputs: {
|
inputs: {
|
||||||
'a|web/index.dart': 'basic_bind_files/index.dart',
|
'a|web/index.dart': 'basic_bind_files/index.dart',
|
||||||
'a|web/bar.dart': 'basic_bind_files/bar.dart'
|
'a|web/bar.dart': 'basic_bind_files/bar.dart'
|
||||||
},
|
},
|
||||||
outputs: {
|
outputs: {
|
||||||
'a|web/index.bootstrap.dart':
|
'a|web/bar.ngDeps.dart': 'basic_bind_files/expected/bar.ngDeps.dart'
|
||||||
'basic_bind_files/expected/index.bootstrap.dart'
|
}),
|
||||||
|
new IntegrationTestConfig('Chained dependencies',
|
||||||
|
inputs: {
|
||||||
|
'a|web/index.dart': 'chained_deps_files/index.dart',
|
||||||
|
'a|web/foo.dart': 'chained_deps_files/foo.dart',
|
||||||
|
'a|web/bar.dart': 'chained_deps_files/bar.dart'
|
||||||
|
},
|
||||||
|
outputs: {
|
||||||
|
'a|web/bar.ngDeps.dart': 'chained_deps_files/expected/bar.ngDeps.dart',
|
||||||
|
'a|web/foo.ngDeps.dart': 'chained_deps_files/expected/foo.ngDeps.dart'
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -118,8 +126,8 @@ void _runTests() {
|
|||||||
config.assetPathToInputPath
|
config.assetPathToInputPath
|
||||||
..addAll(commonInputs)
|
..addAll(commonInputs)
|
||||||
..forEach((key, value) {
|
..forEach((key, value) {
|
||||||
config.assetPathToInputPath[
|
config.assetPathToInputPath[key] =
|
||||||
key] = cache.putIfAbsent(value, () => _readFile(value));
|
cache.putIfAbsent(value, () => _readFile(value));
|
||||||
});
|
});
|
||||||
config.assetPathToExpectedOutputPath.forEach((key, value) {
|
config.assetPathToExpectedOutputPath.forEach((key, value) {
|
||||||
config.assetPathToExpectedOutputPath[key] = cache.putIfAbsent(value, () {
|
config.assetPathToExpectedOutputPath[key] = cache.putIfAbsent(value, () {
|
||||||
@ -133,7 +141,7 @@ void _runTests() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Smoothes over differences in CWD between IDEs and running tests in Travis.
|
/// Smooths over differences in CWD between IDEs and running tests in Travis.
|
||||||
String _readFile(String path) {
|
String _readFile(String path) {
|
||||||
for (var myPath in [path, 'test/transform/${path}']) {
|
for (var myPath in [path, 'test/transform/${path}']) {
|
||||||
var file = new File(myPath);
|
var file = new File(myPath);
|
||||||
@ -141,5 +149,5 @@ String _readFile(String path) {
|
|||||||
return file.readAsStringSync();
|
return file.readAsStringSync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return path;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
library bar;
|
||||||
|
|
||||||
|
import 'bar.dart';
|
||||||
|
import 'package:angular2/src/core/annotations/annotations.dart';
|
||||||
|
import 'package:angular2/src/core/annotations/template.dart';
|
||||||
|
|
||||||
|
bool _visited = false;
|
||||||
|
void setupReflection(reflector) {
|
||||||
|
if (_visited) return;
|
||||||
|
_visited = true;
|
||||||
|
reflector
|
||||||
|
..registerType(MyComponent, {
|
||||||
|
"factory": () => new MyComponent(),
|
||||||
|
"parameters": const [],
|
||||||
|
"annotations": const [
|
||||||
|
const Component(selector: '[soup]'),
|
||||||
|
const Template(inline: 'Salad')
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
@ -1,18 +0,0 @@
|
|||||||
library angular2.src.transform.generated;
|
|
||||||
|
|
||||||
import 'package:angular2/src/reflection/reflection.dart' show reflector;
|
|
||||||
import 'bar.dart' as i0;
|
|
||||||
import 'package:angular2/src/core/annotations/annotations.dart' as i1;
|
|
||||||
import 'package:angular2/src/core/annotations/template.dart' as i2;
|
|
||||||
|
|
||||||
setupReflection() {
|
|
||||||
reflector
|
|
||||||
..registerType(i0.MyComponent, {
|
|
||||||
"factory": () => new i0.MyComponent(),
|
|
||||||
"parameters": const [const []],
|
|
||||||
"annotations": const [
|
|
||||||
const i1.Component(selector: '[soup]'),
|
|
||||||
const i2.Template(inline: 'Salad')
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
|
@ -0,0 +1,20 @@
|
|||||||
|
library bar;
|
||||||
|
|
||||||
|
import 'bar.dart';
|
||||||
|
import 'package:angular2/src/core/annotations/annotations.dart';
|
||||||
|
import 'foo.dart' as prefix;
|
||||||
|
|
||||||
|
bool _visited = false;
|
||||||
|
void setupReflection(reflector) {
|
||||||
|
if (_visited) return;
|
||||||
|
_visited = true;
|
||||||
|
reflector
|
||||||
|
..registerType(MyComponent, {
|
||||||
|
"factory":
|
||||||
|
(prefix.MyContext c, String inValue) => new MyComponent(c, inValue),
|
||||||
|
"parameters": const [const [prefix.MyContext], const [String]],
|
||||||
|
"annotations": const [
|
||||||
|
const Component(selector: prefix.preDefinedSelector)
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
@ -1,16 +0,0 @@
|
|||||||
library angular2.src.transform.generated;
|
|
||||||
|
|
||||||
import 'package:angular2/src/reflection/reflection.dart' show reflector;
|
|
||||||
import 'bar.dart' as i0;
|
|
||||||
import 'foo.dart' as i1;
|
|
||||||
import 'package:angular2/src/core/annotations/annotations.dart' as i2;
|
|
||||||
|
|
||||||
setupReflection() {
|
|
||||||
reflector
|
|
||||||
..registerType(i0.MyComponent, {
|
|
||||||
"factory":
|
|
||||||
(i1.MyContext c, String inValue) => new i0.MyComponent(c, inValue),
|
|
||||||
"parameters": const [const [i1.MyContext, String]],
|
|
||||||
"annotations": const [const i2.Component(selector: i1.preDefinedSelector)]
|
|
||||||
});
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user