feat(dart/transform): Add a `.ng_deps.dart` file parser.
Create a common, reusable `.ng_deps.dart` file parser. All future transformer phases build on the information in `.ng_deps.dart` files.
This commit is contained in:
parent
95c9eca64c
commit
92b22d24d0
|
@ -0,0 +1,26 @@
|
|||
library angular2.src.transform.common.asset_reader;
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:barback/barback.dart';
|
||||
|
||||
abstract class AssetReader {
|
||||
Future<String> readAsString(AssetId id, {Encoding encoding});
|
||||
Future<bool> hasInput(AssetId id);
|
||||
|
||||
/// Creates an [AssetReader] using the `transform`, which should be a
|
||||
/// [Transform] or [AggregateTransform].
|
||||
factory AssetReader.fromTransform(dynamic transform) =>
|
||||
new _TransformAssetReader(transform);
|
||||
}
|
||||
|
||||
class _TransformAssetReader implements AssetReader {
|
||||
final dynamic t;
|
||||
_TransformAssetReader(this.t);
|
||||
|
||||
Future<String> readAsString(AssetId id, {Encoding encoding}) =>
|
||||
t.readInputAsString(id, encoding: encoding);
|
||||
|
||||
Future<bool> hasInput(AssetId id) => t.hasInput(id);
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
library angular2.src.transform.common.parser;
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:analyzer/analyzer.dart';
|
||||
import 'package:angular2/src/transform/common/asset_reader.dart';
|
||||
import 'package:angular2/src/transform/common/logging.dart';
|
||||
import 'package:angular2/src/transform/common/names.dart';
|
||||
import 'package:barback/barback.dart';
|
||||
import 'package:code_transformers/assets.dart';
|
||||
|
||||
import 'registered_type.dart';
|
||||
|
||||
export 'registered_type.dart';
|
||||
|
||||
class Parser {
|
||||
final AssetReader _reader;
|
||||
final _ParseNgDepsVisitor _visitor = new _ParseNgDepsVisitor();
|
||||
|
||||
Parser(AssetReader this._reader);
|
||||
|
||||
Future<List<NgDeps>> parseRecursive(AssetId id) async {
|
||||
return _recurse(id);
|
||||
}
|
||||
|
||||
Future<NgDeps> parse(AssetId id) async {
|
||||
if (!(await _reader.hasInput(id))) return null;
|
||||
var ngDeps = new NgDeps(await _reader.readAsString(id));
|
||||
_visitor.ngDeps = ngDeps;
|
||||
parseCompilationUnit(ngDeps.code, name: id.path).accept(_visitor);
|
||||
return ngDeps;
|
||||
}
|
||||
|
||||
/// Parses the `.ngDeps.dart` file represented by [id] into an [NgDeps]
|
||||
/// object. All `.ngDeps.dart` files imported by [id] are then parsed. The
|
||||
/// results are added to [allDeps].
|
||||
Future<List<NgDeps>> _recurse(AssetId id,
|
||||
[List<NgDeps> allDeps, Set<AssetId> seen]) async {
|
||||
if (seen == null) seen = new Set<AssetId>();
|
||||
if (seen.contains(id)) return;
|
||||
seen.add(id);
|
||||
|
||||
if (allDeps == null) allDeps = [];
|
||||
var ngDeps = await parse(id);
|
||||
allDeps.add(ngDeps);
|
||||
|
||||
var toWait = [];
|
||||
ngDeps.imports.forEach((ImportDirective node) {
|
||||
var uri = stringLiteralToString(node.uri);
|
||||
if (uri.endsWith(DEPS_EXTENSION)) {
|
||||
var importId = uriToAssetId(id, uri, logger, null);
|
||||
toWait.add(_recurse(importId, allDeps, seen));
|
||||
}
|
||||
});
|
||||
return Future.wait(toWait).then((_) => allDeps);
|
||||
}
|
||||
}
|
||||
|
||||
class NgDeps {
|
||||
final String code;
|
||||
final List<ImportDirective> imports = [];
|
||||
final List<ExportDirective> exports = [];
|
||||
final List<RegisteredType> registeredTypes = [];
|
||||
FunctionDeclaration setupMethod;
|
||||
|
||||
NgDeps(this.code);
|
||||
}
|
||||
|
||||
class _ParseNgDepsVisitor extends Object with RecursiveAstVisitor<Object> {
|
||||
NgDeps ngDeps = null;
|
||||
_RegisteredTypeBuilder current = null;
|
||||
|
||||
@override
|
||||
Object visitImportDirective(ImportDirective node) {
|
||||
ngDeps.imports.add(node);
|
||||
return super.visitImportDirective(node);
|
||||
}
|
||||
|
||||
@override
|
||||
Object visitExportDirective(ExportDirective node) {
|
||||
ngDeps.exports.add(node);
|
||||
return super.visitExportDirective(node);
|
||||
}
|
||||
|
||||
@override
|
||||
Object visitFunctionDeclaration(FunctionDeclaration node) {
|
||||
if ('${node.name}' == SETUP_METHOD_NAME) {
|
||||
ngDeps.setupMethod = node;
|
||||
}
|
||||
return super.visitFunctionDeclaration(node);
|
||||
}
|
||||
|
||||
@override
|
||||
Object visitMethodInvocation(MethodInvocation node) {
|
||||
var isRegisterType = '${node.methodName}' == REGISTER_TYPE_METHOD_NAME;
|
||||
|
||||
if (isRegisterType) {
|
||||
ngDeps.registeredTypes.add(new RegisteredType.fromMethodInvocation(node));
|
||||
}
|
||||
|
||||
return super.visitMethodInvocation(node);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
library angular2.src.transform.common.registered_type;
|
||||
|
||||
import 'package:analyzer/analyzer.dart';
|
||||
import 'package:angular2/src/transform/common/names.dart';
|
||||
|
||||
/// Represents a call to `Reflector#registerType` generated by
|
||||
/// `DirectiveProcessor`.
|
||||
class RegisteredType {
|
||||
final Identifier typeName;
|
||||
final MethodInvocation registerMethod;
|
||||
final Expression factory;
|
||||
final Expression parameters;
|
||||
final Expression annotations;
|
||||
|
||||
RegisteredType._(this.typeName, this.registerMethod, this.factory,
|
||||
this.parameters, this.annotations);
|
||||
|
||||
/// Creates a [RegisteredType] given a [MethodInvocation] node representing
|
||||
/// a call to `registerType`.
|
||||
factory RegisteredType.fromMethodInvocation(MethodInvocation registerMethod) {
|
||||
var visitor = new _ParseRegisterTypeVisitor();
|
||||
registerMethod.accept(visitor);
|
||||
return new RegisteredType._(visitor.typeName, registerMethod,
|
||||
visitor.factory, visitor.parameters, visitor.annotations);
|
||||
}
|
||||
}
|
||||
|
||||
class _ParseRegisterTypeVisitor extends Object
|
||||
with RecursiveAstVisitor<Object> {
|
||||
Identifier typeName;
|
||||
Expression factory;
|
||||
Expression parameters;
|
||||
Expression annotations;
|
||||
|
||||
@override
|
||||
Object visitMethodInvocation(MethodInvocation node) {
|
||||
assert('${node.methodName}' == REGISTER_TYPE_METHOD_NAME);
|
||||
|
||||
// The first argument to a `registerType` call is the type.
|
||||
typeName = node.argumentList.arguments[0] is Identifier
|
||||
? node.argumentList.arguments[0]
|
||||
: null;
|
||||
return super.visitMethodInvocation(node);
|
||||
}
|
||||
|
||||
@override
|
||||
Object visitMapLiteralEntry(MapLiteralEntry node) {
|
||||
if (node.key is StringLiteral) {
|
||||
var key = stringLiteralToString(node.key);
|
||||
switch (key) {
|
||||
case 'annotations':
|
||||
annotations = node.value;
|
||||
break;
|
||||
case 'factory':
|
||||
factory = node.value;
|
||||
break;
|
||||
case 'parameters':
|
||||
parameters = node.value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Do not need to descend any further.
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1,7 +1,11 @@
|
|||
library angular2.test.transform.common.read_file;
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:angular2/src/transform/common/asset_reader.dart';
|
||||
import 'package:barback/barback.dart';
|
||||
|
||||
/// Smooths over differences in CWD between IDEs and running tests in Travis.
|
||||
String readFile(String path) {
|
||||
for (var myPath in [path, 'test/transform/${path}']) {
|
||||
|
@ -12,3 +16,17 @@ String readFile(String path) {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
class TestAssetReader implements AssetReader {
|
||||
Future<String> readAsString(AssetId id, {Encoding encoding}) =>
|
||||
new Future.value(readFile(id.path));
|
||||
|
||||
Future<bool> hasInput(AssetId id) {
|
||||
var exists = false;
|
||||
for (var myPath in [id.path, 'test/transform/${id.path}']) {
|
||||
var file = new File(myPath);
|
||||
exists = exists || file.existsSync();
|
||||
}
|
||||
return new Future.value(exists);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue