feat(dart/transform): Reuse readDirectiveMetadata in plugin
Share code for parsing `DirectiveMetadata` values between the transformer and the analyzer plugin.
This commit is contained in:
parent
75e9d3f634
commit
abc8878547
|
@ -5,8 +5,7 @@ import 'types.dart';
|
||||||
import 'dart:mirrors';
|
import 'dart:mirrors';
|
||||||
|
|
||||||
class ReflectionCapabilities {
|
class ReflectionCapabilities {
|
||||||
ReflectionCapabilities([metadataReader]) {
|
ReflectionCapabilities([metadataReader]) {}
|
||||||
}
|
|
||||||
|
|
||||||
Function factory(Type type) {
|
Function factory(Type type) {
|
||||||
ClassMirror classMirror = reflectType(type);
|
ClassMirror classMirror = reflectType(type);
|
||||||
|
|
|
@ -1,30 +1,47 @@
|
||||||
library angular2.transform.common.directive_metadata_reader;
|
library angular2.transform.common.directive_metadata_reader;
|
||||||
|
|
||||||
import 'package:analyzer/analyzer.dart';
|
import 'package:analyzer/analyzer.dart';
|
||||||
|
import 'package:analyzer/src/generated/element.dart';
|
||||||
import 'package:angular2/src/render/api.dart';
|
import 'package:angular2/src/render/api.dart';
|
||||||
import 'logging.dart';
|
|
||||||
import 'parser.dart';
|
|
||||||
|
|
||||||
/// Reads [DirectiveMetadata] from the `attributes` of `t`.
|
/// Reads [DirectiveMetadata] from the `node`. `node` is expected to be an
|
||||||
DirectiveMetadata readDirectiveMetadata(RegisteredType t) {
|
/// instance of [Annotation], [NodeList<Annotation>], ListLiteral, or
|
||||||
|
/// [InstanceCreationExpression].
|
||||||
|
DirectiveMetadata readDirectiveMetadata(dynamic node) {
|
||||||
|
assert(node is Annotation ||
|
||||||
|
node is NodeList ||
|
||||||
|
node is InstanceCreationExpression ||
|
||||||
|
node is ListLiteral);
|
||||||
var visitor = new _DirectiveMetadataVisitor();
|
var visitor = new _DirectiveMetadataVisitor();
|
||||||
t.annotations.accept(visitor);
|
node.accept(visitor);
|
||||||
if (visitor.meta != null) {
|
|
||||||
visitor.meta.id = '${t.typeName}';
|
|
||||||
}
|
|
||||||
return visitor.meta;
|
return visitor.meta;
|
||||||
}
|
}
|
||||||
|
|
||||||
num _getDirectiveType(String annotationName) {
|
num _getDirectiveType(String annotationName, Element element) {
|
||||||
|
var byNameMatch = -1;
|
||||||
// TODO(kegluneq): Detect subtypes & implementations of `Directive`s.
|
// TODO(kegluneq): Detect subtypes & implementations of `Directive`s.
|
||||||
switch (annotationName) {
|
switch (annotationName) {
|
||||||
case 'Directive':
|
case 'Directive':
|
||||||
return DirectiveMetadata.DIRECTIVE_TYPE;
|
byNameMatch = DirectiveMetadata.DIRECTIVE_TYPE;
|
||||||
|
break;
|
||||||
case 'Component':
|
case 'Component':
|
||||||
return DirectiveMetadata.COMPONENT_TYPE;
|
byNameMatch = DirectiveMetadata.COMPONENT_TYPE;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
if (element != null) {
|
||||||
|
var byResolvedAst = -1;
|
||||||
|
var libName = element.library.name;
|
||||||
|
// If we have resolved, ensure the library is correct.
|
||||||
|
if (libName == 'angular2.src.core.annotations.annotations' ||
|
||||||
|
libName == 'angular2.src.core.annotations_impl.annotations') {
|
||||||
|
byResolvedAst = byNameMatch;
|
||||||
|
}
|
||||||
|
// TODO(kegluneq): @keertip, can we expose this as a warning?
|
||||||
|
assert(byNameMatch == byResolvedAst);
|
||||||
|
}
|
||||||
|
return byNameMatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Visitor responsible for processing the `annotations` property of a
|
/// Visitor responsible for processing the `annotations` property of a
|
||||||
|
@ -33,22 +50,45 @@ class _DirectiveMetadataVisitor extends Object
|
||||||
with RecursiveAstVisitor<Object> {
|
with RecursiveAstVisitor<Object> {
|
||||||
DirectiveMetadata meta;
|
DirectiveMetadata meta;
|
||||||
|
|
||||||
@override
|
void _createEmptyMetadata(num type) {
|
||||||
Object visitInstanceCreationExpression(InstanceCreationExpression node) {
|
assert(type >= 0);
|
||||||
var directiveType = _getDirectiveType('${node.constructorName.type.name}');
|
|
||||||
if (directiveType >= 0) {
|
|
||||||
if (meta != null) {
|
|
||||||
logger.error('Only one Directive is allowed per class. '
|
|
||||||
'Found "$node" but already processed "$meta".');
|
|
||||||
}
|
|
||||||
meta = new DirectiveMetadata(
|
meta = new DirectiveMetadata(
|
||||||
type: directiveType,
|
type: type,
|
||||||
compileChildren: true,
|
compileChildren: true,
|
||||||
properties: {},
|
properties: {},
|
||||||
hostListeners: {},
|
hostListeners: {},
|
||||||
hostProperties: {},
|
hostProperties: {},
|
||||||
hostAttributes: {},
|
hostAttributes: {},
|
||||||
readAttributes: []);
|
readAttributes: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object visitAnnotation(Annotation node) {
|
||||||
|
var directiveType = _getDirectiveType('${node.name}', node.element);
|
||||||
|
if (directiveType >= 0) {
|
||||||
|
if (meta != null) {
|
||||||
|
throw new FormatException('Only one Directive is allowed per class. '
|
||||||
|
'Found "$node" but already processed "$meta".',
|
||||||
|
'$node' /* source */);
|
||||||
|
}
|
||||||
|
_createEmptyMetadata(directiveType);
|
||||||
|
super.visitAnnotation(node);
|
||||||
|
}
|
||||||
|
// Annotation we do not recognize - no need to visit.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object visitInstanceCreationExpression(InstanceCreationExpression node) {
|
||||||
|
var directiveType = _getDirectiveType(
|
||||||
|
'${node.constructorName.type.name}', node.staticElement);
|
||||||
|
if (directiveType >= 0) {
|
||||||
|
if (meta != null) {
|
||||||
|
throw new FormatException('Only one Directive is allowed per class. '
|
||||||
|
'Found "$node" but already processed "$meta".',
|
||||||
|
'$node' /* source */);
|
||||||
|
}
|
||||||
|
_createEmptyMetadata(directiveType);
|
||||||
super.visitInstanceCreationExpression(node);
|
super.visitInstanceCreationExpression(node);
|
||||||
}
|
}
|
||||||
// Annotation we do not recognize - no need to visit.
|
// Annotation we do not recognize - no need to visit.
|
||||||
|
@ -59,10 +99,9 @@ class _DirectiveMetadataVisitor extends Object
|
||||||
Object visitNamedExpression(NamedExpression node) {
|
Object visitNamedExpression(NamedExpression node) {
|
||||||
// TODO(kegluneq): Remove this limitation.
|
// TODO(kegluneq): Remove this limitation.
|
||||||
if (node.name is! Label || node.name.label is! SimpleIdentifier) {
|
if (node.name is! Label || node.name.label is! SimpleIdentifier) {
|
||||||
logger.error(
|
throw new FormatException(
|
||||||
'Angular 2 currently only supports simple identifiers in directives.'
|
'Angular 2 currently only supports simple identifiers in directives.',
|
||||||
' Source: ${node}');
|
'$node' /* source */);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
var keyString = '${node.name.label}';
|
var keyString = '${node.name.label}';
|
||||||
// TODO(kegluneq): Populate the other values in [DirectiveMetadata] once
|
// TODO(kegluneq): Populate the other values in [DirectiveMetadata] once
|
||||||
|
@ -93,9 +132,9 @@ class _DirectiveMetadataVisitor extends Object
|
||||||
String _expressionToString(Expression node, String nodeDescription) {
|
String _expressionToString(Expression node, String nodeDescription) {
|
||||||
// TODO(kegluneq): Accept more options.
|
// TODO(kegluneq): Accept more options.
|
||||||
if (node is! SimpleStringLiteral) {
|
if (node is! SimpleStringLiteral) {
|
||||||
logger.error('Angular 2 currently only supports string literals '
|
throw new FormatException(
|
||||||
'in $nodeDescription. Source: ${node}');
|
'Angular 2 currently only supports string literals '
|
||||||
return null;
|
'in $nodeDescription.', '$node' /* source */);
|
||||||
}
|
}
|
||||||
return stringLiteralToString(node);
|
return stringLiteralToString(node);
|
||||||
}
|
}
|
||||||
|
@ -104,23 +143,30 @@ class _DirectiveMetadataVisitor extends Object
|
||||||
meta.selector = _expressionToString(selectorValue, 'Directive#selector');
|
meta.selector = _expressionToString(selectorValue, 'Directive#selector');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _checkMeta() {
|
||||||
|
if (meta == null) {
|
||||||
|
throw new ArgumentError(
|
||||||
|
'Incorrect value passed to readDirectiveMetadata. '
|
||||||
|
'Expected types are Annotation and InstanceCreationExpression');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void _populateCompileChildren(Expression compileChildrenValue) {
|
void _populateCompileChildren(Expression compileChildrenValue) {
|
||||||
|
_checkMeta();
|
||||||
if (compileChildrenValue is! BooleanLiteral) {
|
if (compileChildrenValue is! BooleanLiteral) {
|
||||||
logger.error(
|
throw new FormatException(
|
||||||
'Angular 2 currently only supports boolean literal values for '
|
'Angular 2 currently only supports boolean literal values for '
|
||||||
'Directive#compileChildren.'
|
'Directive#compileChildren.', '$compileChildrenValue' /* source */);
|
||||||
' Source: ${compileChildrenValue}');
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
meta.compileChildren = (compileChildrenValue as BooleanLiteral).value;
|
meta.compileChildren = (compileChildrenValue as BooleanLiteral).value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _populateProperties(Expression propertiesValue) {
|
void _populateProperties(Expression propertiesValue) {
|
||||||
|
_checkMeta();
|
||||||
if (propertiesValue is! MapLiteral) {
|
if (propertiesValue is! MapLiteral) {
|
||||||
logger.error('Angular 2 currently only supports map literal values for '
|
throw new FormatException(
|
||||||
'Directive#properties.'
|
'Angular 2 currently only supports map literal values for '
|
||||||
' Source: ${propertiesValue}');
|
'Directive#properties.', '$propertiesValue' /* source */);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
for (MapLiteralEntry entry in (propertiesValue as MapLiteral).entries) {
|
for (MapLiteralEntry entry in (propertiesValue as MapLiteral).entries) {
|
||||||
var sKey = _expressionToString(entry.key, 'Directive#properties keys');
|
var sKey = _expressionToString(entry.key, 'Directive#properties keys');
|
||||||
|
@ -130,11 +176,11 @@ class _DirectiveMetadataVisitor extends Object
|
||||||
}
|
}
|
||||||
|
|
||||||
void _populateHostListeners(Expression hostListenersValue) {
|
void _populateHostListeners(Expression hostListenersValue) {
|
||||||
|
_checkMeta();
|
||||||
if (hostListenersValue is! MapLiteral) {
|
if (hostListenersValue is! MapLiteral) {
|
||||||
logger.error('Angular 2 currently only supports map literal values for '
|
throw new FormatException(
|
||||||
'Directive#hostListeners.'
|
'Angular 2 currently only supports map literal values for '
|
||||||
' Source: ${hostListenersValue}');
|
'Directive#hostListeners.', '$hostListenersValue' /* source */);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
for (MapLiteralEntry entry in (hostListenersValue as MapLiteral).entries) {
|
for (MapLiteralEntry entry in (hostListenersValue as MapLiteral).entries) {
|
||||||
var sKey = _expressionToString(entry.key, 'Directive#hostListeners keys');
|
var sKey = _expressionToString(entry.key, 'Directive#hostListeners keys');
|
||||||
|
@ -145,11 +191,11 @@ class _DirectiveMetadataVisitor extends Object
|
||||||
}
|
}
|
||||||
|
|
||||||
void _populateHostProperties(Expression hostPropertyValue) {
|
void _populateHostProperties(Expression hostPropertyValue) {
|
||||||
|
_checkMeta();
|
||||||
if (hostPropertyValue is! MapLiteral) {
|
if (hostPropertyValue is! MapLiteral) {
|
||||||
logger.error('Angular 2 currently only supports map literal values for '
|
throw new FormatException(
|
||||||
'Directive#hostProperties.'
|
'Angular 2 currently only supports map literal values for '
|
||||||
' Source: ${hostPropertyValue}');
|
'Directive#hostProperties.', '$hostPropertyValue' /* source */);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
for (MapLiteralEntry entry in (hostPropertyValue as MapLiteral).entries) {
|
for (MapLiteralEntry entry in (hostPropertyValue as MapLiteral).entries) {
|
||||||
var sKey =
|
var sKey =
|
||||||
|
@ -161,11 +207,11 @@ class _DirectiveMetadataVisitor extends Object
|
||||||
}
|
}
|
||||||
|
|
||||||
void _populateHostAttributes(Expression hostAttributeValue) {
|
void _populateHostAttributes(Expression hostAttributeValue) {
|
||||||
|
_checkMeta();
|
||||||
if (hostAttributeValue is! MapLiteral) {
|
if (hostAttributeValue is! MapLiteral) {
|
||||||
logger.error('Angular 2 currently only supports map literal values for '
|
throw new FormatException(
|
||||||
'Directive#hostAttributes.'
|
'Angular 2 currently only supports map literal values for '
|
||||||
' Source: ${hostAttributeValue}');
|
'Directive#hostAttributes.', '$hostAttributeValue' /* source */);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
for (MapLiteralEntry entry in (hostAttributeValue as MapLiteral).entries) {
|
for (MapLiteralEntry entry in (hostAttributeValue as MapLiteral).entries) {
|
||||||
var sKey =
|
var sKey =
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
library angular2.transform.common.registered_type;
|
library angular2.transform.common.registered_type;
|
||||||
|
|
||||||
import 'package:analyzer/analyzer.dart';
|
import 'package:analyzer/analyzer.dart';
|
||||||
|
import 'package:angular2/src/render/api.dart';
|
||||||
|
import 'package:angular2/src/transform/common/directive_metadata_reader.dart';
|
||||||
|
import 'package:angular2/src/transform/common/logging.dart';
|
||||||
import 'package:angular2/src/transform/common/names.dart';
|
import 'package:angular2/src/transform/common/names.dart';
|
||||||
|
|
||||||
/// A call to `Reflector#registerType` generated by `DirectiveProcessor`.
|
/// A call to `Reflector#registerType` generated by `DirectiveProcessor`.
|
||||||
|
@ -16,6 +19,8 @@ class RegisteredType {
|
||||||
/// The annotations registered.
|
/// The annotations registered.
|
||||||
final Expression annotations;
|
final Expression annotations;
|
||||||
|
|
||||||
|
DirectiveMetadata _directiveMetadata = null;
|
||||||
|
|
||||||
RegisteredType._(this.typeName, this.registerMethod, this.factoryFn,
|
RegisteredType._(this.typeName, this.registerMethod, this.factoryFn,
|
||||||
this.parameters, this.annotations);
|
this.parameters, this.annotations);
|
||||||
|
|
||||||
|
@ -27,6 +32,20 @@ class RegisteredType {
|
||||||
return new RegisteredType._(visitor.typeName, registerMethod,
|
return new RegisteredType._(visitor.typeName, registerMethod,
|
||||||
visitor.factoryFn, visitor.parameters, visitor.annotations);
|
visitor.factoryFn, visitor.parameters, visitor.annotations);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DirectiveMetadata get directiveMetadata {
|
||||||
|
if (_directiveMetadata == null) {
|
||||||
|
try {
|
||||||
|
_directiveMetadata = readDirectiveMetadata(annotations);
|
||||||
|
if (_directiveMetadata != null) {
|
||||||
|
_directiveMetadata.id = '$typeName';
|
||||||
|
}
|
||||||
|
} on FormatException catch (ex) {
|
||||||
|
logger.error(ex.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _directiveMetadata;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ParseRegisterTypeVisitor extends Object
|
class _ParseRegisterTypeVisitor extends Object
|
||||||
|
|
|
@ -5,7 +5,6 @@ import 'dart:async';
|
||||||
import 'package:analyzer/analyzer.dart';
|
import 'package:analyzer/analyzer.dart';
|
||||||
import 'package:angular2/src/render/api.dart';
|
import 'package:angular2/src/render/api.dart';
|
||||||
import 'package:angular2/src/transform/common/asset_reader.dart';
|
import 'package:angular2/src/transform/common/asset_reader.dart';
|
||||||
import 'package:angular2/src/transform/common/directive_metadata_reader.dart';
|
|
||||||
import 'package:angular2/src/transform/common/logging.dart';
|
import 'package:angular2/src/transform/common/logging.dart';
|
||||||
import 'package:angular2/src/transform/common/names.dart';
|
import 'package:angular2/src/transform/common/names.dart';
|
||||||
import 'package:angular2/src/transform/common/parser.dart';
|
import 'package:angular2/src/transform/common/parser.dart';
|
||||||
|
@ -58,9 +57,8 @@ Map<String, DirectiveMetadata> _metadataMapFromNgDeps(NgDeps ngDeps) {
|
||||||
if (ngDeps == null || ngDeps.registeredTypes.isEmpty) return null;
|
if (ngDeps == null || ngDeps.registeredTypes.isEmpty) return null;
|
||||||
var retVal = <String, DirectiveMetadata>{};
|
var retVal = <String, DirectiveMetadata>{};
|
||||||
ngDeps.registeredTypes.forEach((rType) {
|
ngDeps.registeredTypes.forEach((rType) {
|
||||||
var meta = readDirectiveMetadata(rType);
|
if (rType.directiveMetadata != null) {
|
||||||
if (meta != null) {
|
retVal['${rType.typeName}'] = rType.directiveMetadata;
|
||||||
retVal['${rType.typeName}'] = meta;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return retVal;
|
return retVal;
|
||||||
|
|
|
@ -24,7 +24,7 @@ void allTests() {
|
||||||
|
|
||||||
Future<DirectiveMetadata> readMetadata(inputPath) async {
|
Future<DirectiveMetadata> readMetadata(inputPath) async {
|
||||||
var ngDeps = await parser.parse(new AssetId('a', inputPath));
|
var ngDeps = await parser.parse(new AssetId('a', inputPath));
|
||||||
return readDirectiveMetadata(ngDeps.registeredTypes.first);
|
return ngDeps.registeredTypes.first.directiveMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('readMetadata', () {
|
describe('readMetadata', () {
|
||||||
|
@ -44,17 +44,17 @@ void allTests() {
|
||||||
// Unset value defaults to `true`.
|
// Unset value defaults to `true`.
|
||||||
it.moveNext();
|
it.moveNext();
|
||||||
expect('${it.current.typeName}').toEqual('UnsetComp');
|
expect('${it.current.typeName}').toEqual('UnsetComp');
|
||||||
var unsetComp = readDirectiveMetadata(it.current);
|
var unsetComp = it.current.directiveMetadata;
|
||||||
expect(unsetComp.compileChildren).toBeTrue();
|
expect(unsetComp.compileChildren).toBeTrue();
|
||||||
|
|
||||||
it.moveNext();
|
it.moveNext();
|
||||||
expect('${it.current.typeName}').toEqual('FalseComp');
|
expect('${it.current.typeName}').toEqual('FalseComp');
|
||||||
var falseComp = readDirectiveMetadata(it.current);
|
var falseComp = it.current.directiveMetadata;
|
||||||
expect(falseComp.compileChildren).toBeFalse();
|
expect(falseComp.compileChildren).toBeFalse();
|
||||||
|
|
||||||
it.moveNext();
|
it.moveNext();
|
||||||
expect('${it.current.typeName}').toEqual('TrueComp');
|
expect('${it.current.typeName}').toEqual('TrueComp');
|
||||||
var trueComp = readDirectiveMetadata(it.current);
|
var trueComp = it.current.directiveMetadata;
|
||||||
expect(trueComp.compileChildren).toBeTrue();
|
expect(trueComp.compileChildren).toBeTrue();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -85,8 +85,8 @@ void allTests() {
|
||||||
var ngDeps = await parser.parse(new AssetId('a',
|
var ngDeps = await parser.parse(new AssetId('a',
|
||||||
'directive_metadata_extractor/'
|
'directive_metadata_extractor/'
|
||||||
'directive_metadata_files/too_many_directives.ng_deps.dart'));
|
'directive_metadata_files/too_many_directives.ng_deps.dart'));
|
||||||
expect(() => readDirectiveMetadata(ngDeps.registeredTypes.first))
|
expect(() => ngDeps.registeredTypes.first.directiveMetadata).toThrowWith(
|
||||||
.toThrowWith(anInstanceOf: PrintLoggerError);
|
anInstanceOf: PrintLoggerError);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
library angular2.src.analysis.analyzer_plugin.src.tasks;
|
library angular2.src.analysis.analyzer_plugin.src.tasks;
|
||||||
|
|
||||||
import 'package:analyzer/src/generated/ast.dart' hide Directive;
|
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/generated/engine.dart';
|
||||||
import 'package:analyzer/src/task/general.dart';
|
import 'package:analyzer/src/task/general.dart';
|
||||||
import 'package:analyzer/task/dart.dart';
|
import 'package:analyzer/task/dart.dart';
|
||||||
import 'package:analyzer/task/model.dart';
|
import 'package:analyzer/task/model.dart';
|
||||||
import 'package:angular2/src/core/annotations/annotations.dart';
|
|
||||||
import 'package:angular2/src/render/api.dart';
|
import 'package:angular2/src/render/api.dart';
|
||||||
|
import 'package:angular2/src/transform/common/directive_metadata_reader.dart';
|
||||||
|
|
||||||
/// The [DirectiveMetadata]s of a [LibrarySpecificUnit].
|
/// The [DirectiveMetadata]s of a [LibrarySpecificUnit].
|
||||||
final ListResultDescriptor<DirectiveMetadata> DIRECTIVES =
|
final ListResultDescriptor<DirectiveMetadata> DIRECTIVES =
|
||||||
|
@ -33,74 +32,15 @@ class BuildUnitDirectivesTask extends SourceBasedAnalysisTask {
|
||||||
List<DirectiveMetadata> metaList = <DirectiveMetadata>[];
|
List<DirectiveMetadata> metaList = <DirectiveMetadata>[];
|
||||||
for (CompilationUnitMember unitMember in unit.declarations) {
|
for (CompilationUnitMember unitMember in unit.declarations) {
|
||||||
if (unitMember is ClassDeclaration) {
|
if (unitMember is ClassDeclaration) {
|
||||||
for (Annotation annotationNode in unitMember.metadata) {
|
DirectiveMetadata meta = readDirectiveMetadata(unitMember.metadata);
|
||||||
Directive directive = _createDirective(annotationNode);
|
if (meta != null) {
|
||||||
if (directive != null) {
|
|
||||||
DirectiveMetadata meta = new DirectiveMetadata(
|
|
||||||
type: _getDirectiveType(directive),
|
|
||||||
selector: directive.selector);
|
|
||||||
metaList.add(meta);
|
metaList.add(meta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
outputs[DIRECTIVES] = metaList;
|
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) {
|
static Map<String, TaskInput> buildInputs(LibrarySpecificUnit target) {
|
||||||
return <String, TaskInput>{UNIT_INPUT: RESOLVED_UNIT.of(target)};
|
return <String, TaskInput>{UNIT_INPUT: RESOLVED_UNIT.of(target)};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue