feat(dart/analysis): Build DirectiveMetadata for LibrarySpecificUnit
initial commit for the dart analyzer task
This commit is contained in:
parent
0856516ae9
commit
0b1bb172c9
|
@ -1,12 +1,14 @@
|
||||||
library angular2.src.analysis.analyzer_plugin;
|
library angular2.src.analysis.analyzer_plugin;
|
||||||
|
|
||||||
import 'package:analyzer/plugin/plugin.dart';
|
import 'package:analyzer/plugin/plugin.dart';
|
||||||
|
import 'package:analyzer/plugin/task.dart';
|
||||||
|
import 'src/tasks.dart';
|
||||||
|
|
||||||
/// Contribute a plugin to the dart analyzer for analysis of
|
/// Contribute a plugin to the dart analyzer for analysis of
|
||||||
/// Angular 2 dart code.
|
/// Angular 2 dart code.
|
||||||
class AngularAnalyzerPlugin implements Plugin {
|
class AngularAnalyzerPlugin implements Plugin {
|
||||||
|
|
||||||
/// the unique indetifier for this plugin
|
/// The unique identifier for this plugin.
|
||||||
static const String UNIQUE_IDENTIFIER = 'angular2.analysis';
|
static const String UNIQUE_IDENTIFIER = 'angular2.analysis';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -17,6 +19,7 @@ class AngularAnalyzerPlugin implements Plugin {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void registerExtensions(RegisterExtension registerExtension) {
|
void registerExtensions(RegisterExtension registerExtension) {
|
||||||
// TODO(keerti): register extension for analysis
|
String taskId = TASK_EXTENSION_POINT_ID;
|
||||||
|
registerExtension(taskId, BuildUnitDirectivesTask.DESCRIPTOR);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
library angular2.src.analysis.analyzer_plugin.src.tasks;
|
||||||
|
|
||||||
|
import 'package:analyzer/src/generated/ast.dart' hide Directive;
|
||||||
|
import 'package:analyzer/src/generated/element.dart';
|
||||||
|
import 'package:analyzer/src/generated/engine.dart';
|
||||||
|
import 'package:analyzer/src/task/general.dart';
|
||||||
|
import 'package:analyzer/task/dart.dart';
|
||||||
|
import 'package:analyzer/task/model.dart';
|
||||||
|
import 'package:angular2/src/core/annotations/annotations.dart';
|
||||||
|
import 'package:angular2/src/render/api.dart';
|
||||||
|
|
||||||
|
/// The [DirectiveMetadata]s of a [LibrarySpecificUnit].
|
||||||
|
final ListResultDescriptor<DirectiveMetadata> DIRECTIVES =
|
||||||
|
new ListResultDescriptor<DirectiveMetadata>('ANGULAR2_DIRECTIVES', null);
|
||||||
|
|
||||||
|
/// A task that builds [DirectiveMetadata]s for directive classes.
|
||||||
|
class BuildUnitDirectivesTask extends SourceBasedAnalysisTask {
|
||||||
|
static const String UNIT_INPUT = 'UNIT_INPUT';
|
||||||
|
|
||||||
|
static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
|
||||||
|
'BuildUnitDirectivesTask', createTask, buildInputs,
|
||||||
|
<ResultDescriptor>[DIRECTIVES]);
|
||||||
|
|
||||||
|
BuildUnitDirectivesTask(AnalysisContext context, AnalysisTarget target)
|
||||||
|
: super(context, target);
|
||||||
|
|
||||||
|
@override
|
||||||
|
TaskDescriptor get descriptor => DESCRIPTOR;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void internalPerform() {
|
||||||
|
CompilationUnit unit = getRequiredInput(UNIT_INPUT);
|
||||||
|
List<DirectiveMetadata> metaList = <DirectiveMetadata>[];
|
||||||
|
for (CompilationUnitMember unitMember in unit.declarations) {
|
||||||
|
if (unitMember is ClassDeclaration) {
|
||||||
|
for (Annotation annotationNode in unitMember.metadata) {
|
||||||
|
Directive directive = _createDirective(annotationNode);
|
||||||
|
if (directive != null) {
|
||||||
|
DirectiveMetadata meta = new DirectiveMetadata(
|
||||||
|
type: _getDirectiveType(directive),
|
||||||
|
selector: directive.selector);
|
||||||
|
metaList.add(meta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outputs[DIRECTIVES] = metaList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an Angular [Directive] that corresponds to the given [node].
|
||||||
|
/// Returns `null` if not an Angular annotation.
|
||||||
|
Directive _createDirective(Annotation node) {
|
||||||
|
// TODO(scheglov) add support for all arguments
|
||||||
|
if (_isAngularAnnotation(node, 'Component')) {
|
||||||
|
String selector = _getNamedArgument(node, 'selector');
|
||||||
|
return new Component(selector: selector);
|
||||||
|
}
|
||||||
|
if (_isAngularAnnotation(node, 'Directive')) {
|
||||||
|
String selector = _getNamedArgument(node, 'selector');
|
||||||
|
return new Directive(selector: selector);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _getDirectiveType(Directive directive) {
|
||||||
|
if (directive is Component) {
|
||||||
|
return DirectiveMetadata.COMPONENT_TYPE;
|
||||||
|
}
|
||||||
|
return DirectiveMetadata.DIRECTIVE_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the value of an argument with the given [name].
|
||||||
|
/// Returns `null` if not found or cannot be evaluated statically.
|
||||||
|
Object _getNamedArgument(Annotation node, String name) {
|
||||||
|
if (node.arguments != null) {
|
||||||
|
List<Expression> arguments = node.arguments.arguments;
|
||||||
|
for (Expression argument in arguments) {
|
||||||
|
if (argument is NamedExpression &&
|
||||||
|
argument.name != null &&
|
||||||
|
argument.name.label != null &&
|
||||||
|
argument.name.label.name == name) {
|
||||||
|
Expression expression = argument.expression;
|
||||||
|
if (expression is SimpleStringLiteral) {
|
||||||
|
return expression.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` is the given [node] is resolved to a creation of an Angular
|
||||||
|
/// annotation class with the given [name].
|
||||||
|
bool _isAngularAnnotation(Annotation node, String name) {
|
||||||
|
if (node.element is ConstructorElement) {
|
||||||
|
ClassElement clazz = node.element.enclosingElement;
|
||||||
|
return clazz.library.name ==
|
||||||
|
'angular2.src.core.annotations.annotations' &&
|
||||||
|
clazz.name == name;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Map<String, TaskInput> buildInputs(LibrarySpecificUnit target) {
|
||||||
|
return <String, TaskInput>{UNIT_INPUT: RESOLVED_UNIT.of(target)};
|
||||||
|
}
|
||||||
|
|
||||||
|
static BuildUnitDirectivesTask createTask(
|
||||||
|
AnalysisContext context, LibrarySpecificUnit target) {
|
||||||
|
return new BuildUnitDirectivesTask(context, target);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
library angular2.src.analysis.analyzer_plugin.tasks;
|
||||||
|
|
||||||
|
import 'package:analyzer/src/generated/error.dart';
|
||||||
|
import 'package:analyzer/task/model.dart';
|
||||||
|
|
||||||
|
/// The analysis errors associated with a target.
|
||||||
|
/// The value combines errors represented by multiple other results.
|
||||||
|
final CompositeResultDescriptor<List<AnalysisError>> HTML_ERRORS =
|
||||||
|
new CompositeResultDescriptor<List<AnalysisError>>('ANGULAR_HTML_ERRORS');
|
|
@ -1,4 +1,4 @@
|
||||||
name: angular2_analysis_plugin
|
name: angular2_analyzer_plugin
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
description: Dart analyzer plugin for Angular 2
|
description: Dart analyzer plugin for Angular 2
|
||||||
environment:
|
environment:
|
||||||
|
@ -6,6 +6,10 @@ environment:
|
||||||
dependencies:
|
dependencies:
|
||||||
angular2: '0.0.0'
|
angular2: '0.0.0'
|
||||||
analyzer: '^0.24.4'
|
analyzer: '^0.24.4'
|
||||||
|
dev_dependencies:
|
||||||
|
unittest: any
|
||||||
|
typed_mock: any
|
||||||
|
test_reflective_loader: any
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
angular2:
|
angular2:
|
||||||
path: ../../dist/dart/angular2
|
path: ../../dist/dart/angular2
|
|
@ -0,0 +1,311 @@
|
||||||
|
library test.src.mock_sdk;
|
||||||
|
|
||||||
|
import 'package:analyzer/file_system/file_system.dart' as resource;
|
||||||
|
import 'package:analyzer/file_system/memory_file_system.dart' as resource;
|
||||||
|
import 'package:analyzer/src/generated/engine.dart';
|
||||||
|
import 'package:analyzer/src/generated/sdk.dart';
|
||||||
|
import 'package:analyzer/src/generated/source.dart';
|
||||||
|
|
||||||
|
class MockSdk implements DartSdk {
|
||||||
|
static const _MockSdkLibrary LIB_CORE = const _MockSdkLibrary('dart:core',
|
||||||
|
'/lib/core/core.dart', '''
|
||||||
|
library dart.core;
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
class Object {
|
||||||
|
bool operator ==(other) => identical(this, other);
|
||||||
|
String toString() => 'a string';
|
||||||
|
int get hashCode => 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Function {}
|
||||||
|
class StackTrace {}
|
||||||
|
class Symbol {}
|
||||||
|
class Type {}
|
||||||
|
|
||||||
|
abstract class Comparable<T> {
|
||||||
|
int compareTo(T other);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class String implements Comparable<String> {
|
||||||
|
external factory String.fromCharCodes(Iterable<int> charCodes,
|
||||||
|
[int start = 0, int end]);
|
||||||
|
bool get isEmpty => false;
|
||||||
|
bool get isNotEmpty => false;
|
||||||
|
int get length => 0;
|
||||||
|
String toUpperCase();
|
||||||
|
List<int> get codeUnits;
|
||||||
|
}
|
||||||
|
|
||||||
|
class bool extends Object {}
|
||||||
|
abstract class num implements Comparable<num> {
|
||||||
|
bool operator <(num other);
|
||||||
|
bool operator <=(num other);
|
||||||
|
bool operator >(num other);
|
||||||
|
bool operator >=(num other);
|
||||||
|
num operator +(num other);
|
||||||
|
num operator -(num other);
|
||||||
|
num operator *(num other);
|
||||||
|
num operator /(num other);
|
||||||
|
int toInt();
|
||||||
|
num abs();
|
||||||
|
int round();
|
||||||
|
}
|
||||||
|
abstract class int extends num {
|
||||||
|
bool get isEven => false;
|
||||||
|
int operator -();
|
||||||
|
external static int parse(String source,
|
||||||
|
{ int radix,
|
||||||
|
int onError(String source) });
|
||||||
|
}
|
||||||
|
class double extends num {}
|
||||||
|
class DateTime extends Object {}
|
||||||
|
class Null extends Object {}
|
||||||
|
|
||||||
|
class Deprecated extends Object {
|
||||||
|
final String expires;
|
||||||
|
const Deprecated(this.expires);
|
||||||
|
}
|
||||||
|
const Object deprecated = const Deprecated("next release");
|
||||||
|
|
||||||
|
class Iterator<E> {
|
||||||
|
bool moveNext();
|
||||||
|
E get current;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class Iterable<E> {
|
||||||
|
Iterator<E> get iterator;
|
||||||
|
bool get isEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class List<E> implements Iterable<E> {
|
||||||
|
void add(E value);
|
||||||
|
E operator [](int index);
|
||||||
|
void operator []=(int index, E value);
|
||||||
|
Iterator<E> get iterator => null;
|
||||||
|
void clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class Map<K, V> extends Object {
|
||||||
|
Iterable<K> get keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
external bool identical(Object a, Object b);
|
||||||
|
|
||||||
|
void print(Object object) {}
|
||||||
|
|
||||||
|
class _Override {
|
||||||
|
const _Override();
|
||||||
|
}
|
||||||
|
const Object override = const _Override();
|
||||||
|
''');
|
||||||
|
|
||||||
|
static const _MockSdkLibrary LIB_ASYNC = const _MockSdkLibrary('dart:async',
|
||||||
|
'/lib/async/async.dart', '''
|
||||||
|
library dart.async;
|
||||||
|
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
class Future<T> {
|
||||||
|
factory Future.delayed(Duration duration, [T computation()]) => null;
|
||||||
|
factory Future.value([value]) => null;
|
||||||
|
static Future wait(List<Future> futures) => null;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Stream<T> {}
|
||||||
|
abstract class StreamTransformer<S, T> {}
|
||||||
|
''');
|
||||||
|
|
||||||
|
static const _MockSdkLibrary LIB_COLLECTION = const _MockSdkLibrary(
|
||||||
|
'dart:collection', '/lib/collection/collection.dart', '''
|
||||||
|
library dart.collection;
|
||||||
|
|
||||||
|
abstract class HashMap<K, V> implements Map<K, V> {}
|
||||||
|
''');
|
||||||
|
|
||||||
|
static const _MockSdkLibrary LIB_CONVERT = const _MockSdkLibrary(
|
||||||
|
'dart:convert', '/lib/convert/convert.dart', '''
|
||||||
|
library dart.convert;
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
abstract class Converter<S, T> implements StreamTransformer {}
|
||||||
|
class JsonDecoder extends Converter<String, Object> {}
|
||||||
|
''');
|
||||||
|
|
||||||
|
static const _MockSdkLibrary LIB_MATH = const _MockSdkLibrary('dart:math',
|
||||||
|
'/lib/math/math.dart', '''
|
||||||
|
library dart.math;
|
||||||
|
const double E = 2.718281828459045;
|
||||||
|
const double PI = 3.1415926535897932;
|
||||||
|
const double LN10 = 2.302585092994046;
|
||||||
|
num min(num a, num b) => 0;
|
||||||
|
num max(num a, num b) => 0;
|
||||||
|
external double cos(num x);
|
||||||
|
external double sin(num x);
|
||||||
|
external double sqrt(num x);
|
||||||
|
class Random {
|
||||||
|
bool nextBool() => true;
|
||||||
|
double nextDouble() => 2.0;
|
||||||
|
int nextInt() => 1;
|
||||||
|
}
|
||||||
|
''');
|
||||||
|
|
||||||
|
static const _MockSdkLibrary LIB_HTML = const _MockSdkLibrary('dart:html',
|
||||||
|
'/lib/html/dartium/html_dartium.dart', '''
|
||||||
|
library dart.html;
|
||||||
|
class HtmlElement {}
|
||||||
|
''');
|
||||||
|
|
||||||
|
static const List<SdkLibrary> LIBRARIES = const [
|
||||||
|
LIB_CORE,
|
||||||
|
LIB_ASYNC,
|
||||||
|
LIB_COLLECTION,
|
||||||
|
LIB_CONVERT,
|
||||||
|
LIB_MATH,
|
||||||
|
LIB_HTML,
|
||||||
|
];
|
||||||
|
|
||||||
|
final resource.MemoryResourceProvider provider =
|
||||||
|
new resource.MemoryResourceProvider();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [AnalysisContext] which is used for all of the sources.
|
||||||
|
*/
|
||||||
|
InternalAnalysisContext _analysisContext;
|
||||||
|
|
||||||
|
MockSdk() {
|
||||||
|
LIBRARIES.forEach((_MockSdkLibrary library) {
|
||||||
|
provider.newFile(library.path, library.content);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
AnalysisContext get context {
|
||||||
|
if (_analysisContext == null) {
|
||||||
|
_analysisContext = new SdkAnalysisContext();
|
||||||
|
SourceFactory factory = new SourceFactory([new DartUriResolver(this)]);
|
||||||
|
_analysisContext.sourceFactory = factory;
|
||||||
|
ChangeSet changeSet = new ChangeSet();
|
||||||
|
for (String uri in uris) {
|
||||||
|
Source source = factory.forUri(uri);
|
||||||
|
changeSet.addedSource(source);
|
||||||
|
}
|
||||||
|
_analysisContext.applyChanges(changeSet);
|
||||||
|
}
|
||||||
|
return _analysisContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<SdkLibrary> get sdkLibraries => LIBRARIES;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get sdkVersion => throw unimplemented;
|
||||||
|
|
||||||
|
UnimplementedError get unimplemented => new UnimplementedError();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<String> get uris {
|
||||||
|
List<String> uris = <String>[];
|
||||||
|
for (SdkLibrary library in LIBRARIES) {
|
||||||
|
uris.add(library.shortName);
|
||||||
|
}
|
||||||
|
return uris;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Source fromFileUri(Uri uri) {
|
||||||
|
String filePath = uri.path;
|
||||||
|
String libPath = '/lib';
|
||||||
|
if (!filePath.startsWith("$libPath/")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (SdkLibrary library in LIBRARIES) {
|
||||||
|
String libraryPath = library.path;
|
||||||
|
if (filePath.replaceAll('\\', '/') == libraryPath) {
|
||||||
|
try {
|
||||||
|
resource.File file = provider.getResource(uri.path);
|
||||||
|
Uri dartUri = Uri.parse(library.shortName);
|
||||||
|
return file.createSource(dartUri);
|
||||||
|
} catch (exception) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (filePath.startsWith("$libraryPath/")) {
|
||||||
|
String pathInLibrary = filePath.substring(libraryPath.length + 1);
|
||||||
|
String path = '${library.shortName}/${pathInLibrary}';
|
||||||
|
try {
|
||||||
|
resource.File file = provider.getResource(uri.path);
|
||||||
|
Uri dartUri = new Uri(scheme: 'dart', path: path);
|
||||||
|
return file.createSource(dartUri);
|
||||||
|
} catch (exception) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
SdkLibrary getSdkLibrary(String dartUri) {
|
||||||
|
// getSdkLibrary() is only used to determine whether a library is internal
|
||||||
|
// to the SDK. The mock SDK doesn't have any internals, so it's safe to
|
||||||
|
// return null.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Source mapDartUri(String dartUri) {
|
||||||
|
const Map<String, String> uriToPath = const {
|
||||||
|
"dart:core": "/lib/core/core.dart",
|
||||||
|
"dart:html": "/lib/html/dartium/html_dartium.dart",
|
||||||
|
"dart:async": "/lib/async/async.dart",
|
||||||
|
"dart:collection": "/lib/collection/collection.dart",
|
||||||
|
"dart:convert": "/lib/convert/convert.dart",
|
||||||
|
"dart:math": "/lib/math/math.dart"
|
||||||
|
};
|
||||||
|
|
||||||
|
String path = uriToPath[dartUri];
|
||||||
|
if (path != null) {
|
||||||
|
resource.File file = provider.getResource(path);
|
||||||
|
Uri uri = new Uri(scheme: 'dart', path: dartUri.substring(5));
|
||||||
|
return file.createSource(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we reach here then we tried to use a dartUri that's not in the
|
||||||
|
// table above.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MockSdkLibrary implements SdkLibrary {
|
||||||
|
final String shortName;
|
||||||
|
final String path;
|
||||||
|
final String content;
|
||||||
|
|
||||||
|
const _MockSdkLibrary(this.shortName, this.path, this.content);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get category => throw unimplemented;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get isDart2JsLibrary => throw unimplemented;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get isDocumented => throw unimplemented;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get isImplementation => throw unimplemented;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get isInternal => throw unimplemented;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get isShared => throw unimplemented;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get isVmLibrary => throw unimplemented;
|
||||||
|
|
||||||
|
UnimplementedError get unimplemented => new UnimplementedError();
|
||||||
|
}
|
|
@ -0,0 +1,205 @@
|
||||||
|
library angular2.src.analysis.analyzer_plugin.src.tasks_test;
|
||||||
|
|
||||||
|
import 'package:analyzer/file_system/file_system.dart';
|
||||||
|
import 'package:analyzer/file_system/memory_file_system.dart';
|
||||||
|
import 'package:analyzer/src/context/cache.dart';
|
||||||
|
import 'package:analyzer/src/generated/engine.dart'
|
||||||
|
show AnalysisOptionsImpl, TimestampedData;
|
||||||
|
import 'package:analyzer/src/generated/resolver.dart';
|
||||||
|
import 'package:analyzer/src/generated/sdk.dart';
|
||||||
|
import 'package:analyzer/src/generated/source.dart';
|
||||||
|
import 'package:analyzer/src/task/dart.dart';
|
||||||
|
import 'package:analyzer/src/task/driver.dart';
|
||||||
|
import 'package:analyzer/src/task/general.dart';
|
||||||
|
import 'package:analyzer/src/task/manager.dart';
|
||||||
|
import 'package:analyzer/task/dart.dart';
|
||||||
|
import 'package:analyzer/task/model.dart';
|
||||||
|
import 'package:angular2/src/render/api.dart';
|
||||||
|
import 'package:angular2_analyzer_plugin/src/tasks.dart';
|
||||||
|
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||||
|
import 'package:typed_mock/typed_mock.dart';
|
||||||
|
import 'package:unittest/unittest.dart';
|
||||||
|
|
||||||
|
import 'mock_sdk.dart';
|
||||||
|
|
||||||
|
main() {
|
||||||
|
groupSep = ' | ';
|
||||||
|
defineReflectiveTests(BuildUnitDirectivesTaskTest);
|
||||||
|
}
|
||||||
|
|
||||||
|
@reflectiveTest
|
||||||
|
class BuildUnitDirectivesTaskTest extends _AbstractDartTaskTest {
|
||||||
|
MemoryResourceProvider resourceProvider = new MemoryResourceProvider();
|
||||||
|
|
||||||
|
void test_Component() {
|
||||||
|
_addAngularSources();
|
||||||
|
Source source = _newSource('/test.dart', r'''
|
||||||
|
import '/angular2/annotations.dart';
|
||||||
|
|
||||||
|
@Component(selector: 'comp-a')
|
||||||
|
class ComponentA {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component(selector: 'comp-b')
|
||||||
|
class ComponentB {
|
||||||
|
}
|
||||||
|
''');
|
||||||
|
LibrarySpecificUnit target = new LibrarySpecificUnit(source, source);
|
||||||
|
_computeResult(target, DIRECTIVES);
|
||||||
|
expect(task, new isInstanceOf<BuildUnitDirectivesTask>());
|
||||||
|
// validate
|
||||||
|
List<DirectiveMetadata> directives = outputs[DIRECTIVES];
|
||||||
|
expect(directives, hasLength(2));
|
||||||
|
expect(directives[0].selector, 'comp-a');
|
||||||
|
expect(directives[1].selector, 'comp-b');
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_Directive() {
|
||||||
|
_addAngularSources();
|
||||||
|
Source source = _newSource('/test.dart', r'''
|
||||||
|
import '/angular2/annotations.dart';
|
||||||
|
|
||||||
|
@Directive(selector: 'deco-a')
|
||||||
|
class ComponentA {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Directive(selector: 'deco-b')
|
||||||
|
class ComponentB {
|
||||||
|
}
|
||||||
|
''');
|
||||||
|
LibrarySpecificUnit target = new LibrarySpecificUnit(source, source);
|
||||||
|
_computeResult(target, DIRECTIVES);
|
||||||
|
expect(task, new isInstanceOf<BuildUnitDirectivesTask>());
|
||||||
|
// validate
|
||||||
|
List<DirectiveMetadata> directives = outputs[DIRECTIVES];
|
||||||
|
expect(directives, hasLength(2));
|
||||||
|
expect(directives[0].selector, 'deco-a');
|
||||||
|
expect(directives[1].selector, 'deco-b');
|
||||||
|
}
|
||||||
|
|
||||||
|
void _addAngularSources() {
|
||||||
|
_newSource('/angular2/annotations.dart', r'''
|
||||||
|
library angular2.src.core.annotations.annotations;
|
||||||
|
|
||||||
|
abstract class Directive {
|
||||||
|
final String selector;
|
||||||
|
final dynamic properties;
|
||||||
|
final dynamic hostListeners;
|
||||||
|
final List lifecycle;
|
||||||
|
const Directive({selector, properties, hostListeners, lifecycle})
|
||||||
|
: selector = selector,
|
||||||
|
properties = properties,
|
||||||
|
hostListeners = hostListeners,
|
||||||
|
lifecycle = lifecycle,
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
class Component extends Directive {
|
||||||
|
final String changeDetection;
|
||||||
|
final List injectables;
|
||||||
|
const Component({selector, properties, events, hostListeners,
|
||||||
|
injectables, lifecycle, changeDetection: 'DEFAULT'})
|
||||||
|
: changeDetection = changeDetection,
|
||||||
|
injectables = injectables,
|
||||||
|
super(
|
||||||
|
selector: selector,
|
||||||
|
properties: properties,
|
||||||
|
events: events,
|
||||||
|
hostListeners: hostListeners,
|
||||||
|
lifecycle: lifecycle);
|
||||||
|
}
|
||||||
|
|
||||||
|
''');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AbstractDartTaskTest {
|
||||||
|
MemoryResourceProvider resourceProvider = new MemoryResourceProvider();
|
||||||
|
Source emptySource;
|
||||||
|
|
||||||
|
DartSdk sdk = new MockSdk();
|
||||||
|
_MockContext context = new _MockContext();
|
||||||
|
Map<AnalysisTarget, CacheEntry> entryMap = <AnalysisTarget, CacheEntry>{};
|
||||||
|
|
||||||
|
TaskManager taskManager = new TaskManager();
|
||||||
|
AnalysisDriver analysisDriver;
|
||||||
|
|
||||||
|
AnalysisTask task;
|
||||||
|
Map<ResultDescriptor<dynamic>, dynamic> outputs;
|
||||||
|
|
||||||
|
CacheEntry getCacheEntry(AnalysisTarget target) {
|
||||||
|
return entryMap.putIfAbsent(target, () => new CacheEntry());
|
||||||
|
}
|
||||||
|
|
||||||
|
void setUp() {
|
||||||
|
emptySource = _newSource('/test.dart');
|
||||||
|
// prepare AnalysisContext
|
||||||
|
context.sourceFactory = new SourceFactory(<UriResolver>[
|
||||||
|
new DartUriResolver(sdk),
|
||||||
|
new ResourceUriResolver(resourceProvider)
|
||||||
|
]);
|
||||||
|
// prepare TaskManager
|
||||||
|
taskManager.addTaskDescriptor(GetContentTask.DESCRIPTOR);
|
||||||
|
// TODO(scheglov) extract into API
|
||||||
|
taskManager.addTaskDescriptor(ScanDartTask.DESCRIPTOR);
|
||||||
|
taskManager.addTaskDescriptor(ParseDartTask.DESCRIPTOR);
|
||||||
|
taskManager.addTaskDescriptor(BuildClassConstructorsTask.DESCRIPTOR);
|
||||||
|
taskManager.addTaskDescriptor(BuildCompilationUnitElementTask.DESCRIPTOR);
|
||||||
|
taskManager.addTaskDescriptor(BuildLibraryConstructorsTask.DESCRIPTOR);
|
||||||
|
taskManager.addTaskDescriptor(BuildLibraryElementTask.DESCRIPTOR);
|
||||||
|
taskManager.addTaskDescriptor(BuildPublicNamespaceTask.DESCRIPTOR);
|
||||||
|
taskManager.addTaskDescriptor(BuildDirectiveElementsTask.DESCRIPTOR);
|
||||||
|
taskManager.addTaskDescriptor(BuildSourceClosuresTask.DESCRIPTOR);
|
||||||
|
taskManager.addTaskDescriptor(BuildExportNamespaceTask.DESCRIPTOR);
|
||||||
|
taskManager.addTaskDescriptor(BuildEnumMemberElementsTask.DESCRIPTOR);
|
||||||
|
taskManager.addTaskDescriptor(BuildFunctionTypeAliasesTask.DESCRIPTOR);
|
||||||
|
taskManager.addTaskDescriptor(BuildTypeProviderTask.DESCRIPTOR);
|
||||||
|
taskManager.addTaskDescriptor(GatherUsedImportedElementsTask.DESCRIPTOR);
|
||||||
|
taskManager.addTaskDescriptor(GatherUsedLocalElementsTask.DESCRIPTOR);
|
||||||
|
taskManager.addTaskDescriptor(GenerateHintsTask.DESCRIPTOR);
|
||||||
|
taskManager.addTaskDescriptor(ResolveUnitTypeNamesTask.DESCRIPTOR);
|
||||||
|
taskManager.addTaskDescriptor(ResolveLibraryTypeNamesTask.DESCRIPTOR);
|
||||||
|
taskManager.addTaskDescriptor(ResolveReferencesTask.DESCRIPTOR);
|
||||||
|
taskManager.addTaskDescriptor(ResolveVariableReferencesTask.DESCRIPTOR);
|
||||||
|
taskManager.addTaskDescriptor(VerifyUnitTask.DESCRIPTOR);
|
||||||
|
// Angular specific tasks
|
||||||
|
taskManager.addTaskDescriptor(BuildUnitDirectivesTask.DESCRIPTOR);
|
||||||
|
// prepare AnalysisDriver
|
||||||
|
analysisDriver = new AnalysisDriver(taskManager, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _computeResult(AnalysisTarget target, ResultDescriptor result) {
|
||||||
|
task = analysisDriver.computeResult(target, result);
|
||||||
|
expect(task.caughtException, isNull);
|
||||||
|
outputs = task.outputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
Source _newSource(String path, [String content = '']) {
|
||||||
|
File file = resourceProvider.newFile(path, content);
|
||||||
|
return file.createSource();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MockContext extends TypedMock implements ExtendedAnalysisContext {
|
||||||
|
AnalysisOptionsImpl analysisOptions = new AnalysisOptionsImpl();
|
||||||
|
SourceFactory sourceFactory;
|
||||||
|
TypeProvider typeProvider;
|
||||||
|
|
||||||
|
Map<AnalysisTarget, CacheEntry> entryMap = <AnalysisTarget, CacheEntry>{};
|
||||||
|
|
||||||
|
String get name => '_MockContext';
|
||||||
|
|
||||||
|
bool exists(Source source) => source.exists();
|
||||||
|
|
||||||
|
@override
|
||||||
|
CacheEntry getCacheEntry(AnalysisTarget target) {
|
||||||
|
return entryMap.putIfAbsent(target, () => new CacheEntry());
|
||||||
|
}
|
||||||
|
|
||||||
|
TimestampedData<String> getContents(Source source) => source.contents;
|
||||||
|
|
||||||
|
noSuchMethod(Invocation invocation) {
|
||||||
|
print('noSuchMethod: ${invocation.memberName}');
|
||||||
|
return super.noSuchMethod(invocation);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue