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;
|
library angular2.test.transform.common.read_file;
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
import 'dart:io';
|
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.
|
/// 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}']) {
|
||||||
|
@ -12,3 +16,17 @@ String readFile(String path) {
|
||||||
}
|
}
|
||||||
return null;
|
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