fix(transformer): Support prefixed annotations in the transformer.
closes https://github.com/angular/angular/issues/2754
This commit is contained in:
parent
569766fa8b
commit
9e1158de4f
|
@ -127,17 +127,37 @@ class AnnotationMatcher {
|
||||||
// Checks if an [Annotation] matches an [AnnotationDescriptor].
|
// Checks if an [Annotation] matches an [AnnotationDescriptor].
|
||||||
static bool _matchAnnotation(
|
static bool _matchAnnotation(
|
||||||
Annotation annotation, AnnotationDescriptor descriptor, AssetId assetId) {
|
Annotation annotation, AnnotationDescriptor descriptor, AssetId assetId) {
|
||||||
if (annotation.name.name != descriptor.name) return false;
|
String name;
|
||||||
|
Identifier prefix;
|
||||||
|
if (annotation.name is PrefixedIdentifier) {
|
||||||
|
// TODO(jakemac): Shouldn't really need a cast here, remove once
|
||||||
|
// https://github.com/dart-lang/sdk/issues/23798 is fixed.
|
||||||
|
var prefixedName = annotation.name as PrefixedIdentifier;
|
||||||
|
name = prefixedName.identifier.name;
|
||||||
|
prefix = prefixedName.prefix;
|
||||||
|
} else {
|
||||||
|
name = annotation.name.name;
|
||||||
|
}
|
||||||
|
if (name != descriptor.name) return false;
|
||||||
return (annotation.root as CompilationUnit).directives
|
return (annotation.root as CompilationUnit).directives
|
||||||
.where((d) => d is ImportDirective)
|
.where((d) => d is ImportDirective)
|
||||||
.any((ImportDirective i) {
|
.any((ImportDirective i) {
|
||||||
|
var importMatch = false;
|
||||||
var uriString = i.uri.stringValue;
|
var uriString = i.uri.stringValue;
|
||||||
if (uriString == descriptor.import) return true;
|
if (uriString == descriptor.import) {
|
||||||
if (uriString.startsWith('package:') || uriString.startsWith('dart:')) {
|
importMatch = true;
|
||||||
|
} else if (uriString.startsWith('package:') ||
|
||||||
|
uriString.startsWith('dart:')) {
|
||||||
return false;
|
return false;
|
||||||
}
|
} else {
|
||||||
return descriptor.assetId ==
|
importMatch = descriptor.assetId ==
|
||||||
uriToAssetId(assetId, uriString, logger, null);
|
uriToAssetId(assetId, uriString, logger, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!importMatch) return false;
|
||||||
|
if (prefix == null) return i.prefix == null;
|
||||||
|
if (i.prefix == null) return false;
|
||||||
|
return prefix.name == i.prefix.name;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,8 +36,13 @@ class ViewDefinitionEntry {
|
||||||
String _getComponentId(AssetId assetId, String className) => '$className';
|
String _getComponentId(AssetId assetId, String className) => '$className';
|
||||||
|
|
||||||
// TODO(kegluenq): Improve this test.
|
// TODO(kegluenq): Improve this test.
|
||||||
bool _isViewAnnotation(InstanceCreationExpression node) =>
|
bool _isViewAnnotation(InstanceCreationExpression node) {
|
||||||
'${node.constructorName.type}' == 'View';
|
var constructorName = node.constructorName.type.name;
|
||||||
|
if (constructorName is PrefixedIdentifier) {
|
||||||
|
constructorName = constructorName.identifier;
|
||||||
|
}
|
||||||
|
return constructorName.name == 'View';
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates [ViewDefinition] objects for all `View` `Directive`s defined in
|
/// Creates [ViewDefinition] objects for all `View` `Directive`s defined in
|
||||||
/// `entryPoint`.
|
/// `entryPoint`.
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
library angular2.test.transform.common.annotation_matcher_test;
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'package:analyzer/analyzer.dart';
|
||||||
|
import 'package:angular2/src/transform/common/annotation_matcher.dart';
|
||||||
|
import 'package:barback/barback.dart' show AssetId;
|
||||||
|
import 'package:guinness/guinness.dart';
|
||||||
|
|
||||||
|
main() {
|
||||||
|
allTests();
|
||||||
|
}
|
||||||
|
|
||||||
|
var simpleAst = parseCompilationUnit('''
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
@Test()
|
||||||
|
var foo;
|
||||||
|
''');
|
||||||
|
|
||||||
|
var namespacedAst = parseCompilationUnit('''
|
||||||
|
import 'package:test/test.dart' as test;
|
||||||
|
|
||||||
|
@test.Test()
|
||||||
|
var foo;
|
||||||
|
''');
|
||||||
|
|
||||||
|
var relativePathAst = parseCompilationUnit('''
|
||||||
|
import 'test.dart';
|
||||||
|
|
||||||
|
@Test()
|
||||||
|
var foo;
|
||||||
|
''');
|
||||||
|
|
||||||
|
var namespacedRelativePathAst = parseCompilationUnit('''
|
||||||
|
import 'test.dart' as test;
|
||||||
|
|
||||||
|
@test.Test()
|
||||||
|
var foo;
|
||||||
|
''');
|
||||||
|
|
||||||
|
void allTests() {
|
||||||
|
it('should be able to match basic annotations.', () {
|
||||||
|
var matcher = new AnnotationMatcher()
|
||||||
|
..add(const AnnotationDescriptor('Test', 'package:test/test.dart', null));
|
||||||
|
var visitor = new MatchRecordingVisitor(matcher);
|
||||||
|
simpleAst.accept(visitor);
|
||||||
|
expect(visitor.matches.length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to match namespaced annotations.', () {
|
||||||
|
var matcher = new AnnotationMatcher()
|
||||||
|
..add(const AnnotationDescriptor('Test', 'package:test/test.dart', null));
|
||||||
|
var visitor = new MatchRecordingVisitor(matcher);
|
||||||
|
namespacedAst.accept(visitor);
|
||||||
|
expect(visitor.matches.length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to match relative imports.', () {
|
||||||
|
var matcher = new AnnotationMatcher()
|
||||||
|
..add(const AnnotationDescriptor('Test', 'package:test/test.dart', null));
|
||||||
|
var visitor =
|
||||||
|
new MatchRecordingVisitor(matcher, new AssetId('test', 'lib/foo.dart'));
|
||||||
|
relativePathAst.accept(visitor);
|
||||||
|
expect(visitor.matches.length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to match relative imports with a namespace.', () {
|
||||||
|
var matcher = new AnnotationMatcher()
|
||||||
|
..add(const AnnotationDescriptor('Test', 'package:test/test.dart', null));
|
||||||
|
var visitor =
|
||||||
|
new MatchRecordingVisitor(matcher, new AssetId('test', 'lib/foo.dart'));
|
||||||
|
namespacedRelativePathAst.accept(visitor);
|
||||||
|
expect(visitor.matches.length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not match annotations if the import is missing.', () {
|
||||||
|
var matcher = new AnnotationMatcher()
|
||||||
|
..add(const AnnotationDescriptor('Test', 'package:test/foo.dart', null));
|
||||||
|
var visitor = new MatchRecordingVisitor(matcher);
|
||||||
|
simpleAst.accept(visitor);
|
||||||
|
expect(visitor.matches.isEmpty).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not match annotations if the name is different.', () {
|
||||||
|
var matcher = new AnnotationMatcher()
|
||||||
|
..add(const AnnotationDescriptor('Foo', 'package:test/test.dart', null));
|
||||||
|
var visitor = new MatchRecordingVisitor(matcher);
|
||||||
|
simpleAst.accept(visitor);
|
||||||
|
expect(visitor.matches.isEmpty).toBeTrue();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class MatchRecordingVisitor extends RecursiveAstVisitor {
|
||||||
|
final AssetId assetId;
|
||||||
|
final AnnotationMatcher matcher;
|
||||||
|
final List<Annotation> matches = [];
|
||||||
|
|
||||||
|
MatchRecordingVisitor(this.matcher, [this.assetId]) : super();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void visitAnnotation(Annotation annotation) {
|
||||||
|
if (matcher.hasMatch(annotation, assetId)) matches.add(annotation);
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,8 @@ import '../common/read_file.dart';
|
||||||
|
|
||||||
var formatter = new DartFormatter();
|
var formatter = new DartFormatter();
|
||||||
|
|
||||||
|
main() => allTests();
|
||||||
|
|
||||||
void allTests() {
|
void allTests() {
|
||||||
Html5LibDomAdapter.makeCurrent();
|
Html5LibDomAdapter.makeCurrent();
|
||||||
AssetReader reader = new TestAssetReader();
|
AssetReader reader = new TestAssetReader();
|
||||||
|
@ -93,6 +95,16 @@ void allTests() {
|
||||||
_formatThenExpectEquals(output, expected);
|
_formatThenExpectEquals(output, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should parse angular directives with a prefix', () async {
|
||||||
|
var inputPath =
|
||||||
|
'template_compiler/with_prefix_files/ng2_prefix.ng_deps.dart';
|
||||||
|
var expected = readFile(
|
||||||
|
'template_compiler/with_prefix_files/expected/ng2_prefix.ng_deps.dart');
|
||||||
|
|
||||||
|
var output = await process(new AssetId('a', inputPath));
|
||||||
|
_formatThenExpectEquals(output, expected);
|
||||||
|
});
|
||||||
|
|
||||||
it('should create the same output for multiple calls.', () async {
|
it('should create the same output for multiple calls.', () async {
|
||||||
var inputPath =
|
var inputPath =
|
||||||
'template_compiler/inline_expression_files/hello.ng_deps.dart';
|
'template_compiler/inline_expression_files/hello.ng_deps.dart';
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
library angular2.test.transform.template_compiler.with_prefix_files.ng2_prefix.ng_deps.dart;
|
||||||
|
|
||||||
|
import 'ng2_prefix.dart';
|
||||||
|
import 'package:angular2/angular2.dart' as ng2
|
||||||
|
show bootstrap, Component, Directive, View, NgElement;
|
||||||
|
|
||||||
|
var _visited = false;
|
||||||
|
void initReflector(reflector) {
|
||||||
|
if (_visited) return;
|
||||||
|
_visited = true;
|
||||||
|
reflector
|
||||||
|
..registerType(MyApp, {
|
||||||
|
'factory': () => new MyApp(),
|
||||||
|
'parameters': const [const []],
|
||||||
|
'annotations': const [
|
||||||
|
const ng2.Component(selector: 'my-app'),
|
||||||
|
const ng2.View(template: 'MyApp {{name}}')
|
||||||
|
]
|
||||||
|
})
|
||||||
|
..registerGetters({'name': (o) => o.name})
|
||||||
|
..registerSetters({'name': (o, v) => o.name = v});
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
library angular2.test.transform.template_compiler.with_prefix_files.ng2_prefix.ng_deps.dart;
|
||||||
|
|
||||||
|
import 'ng2_prefix.dart';
|
||||||
|
import 'package:angular2/angular2.dart' as ng2
|
||||||
|
show bootstrap, Component, Directive, View, NgElement;
|
||||||
|
|
||||||
|
var _visited = false;
|
||||||
|
void initReflector(reflector) {
|
||||||
|
if (_visited) return;
|
||||||
|
_visited = true;
|
||||||
|
reflector
|
||||||
|
..registerType(MyApp, {
|
||||||
|
'factory': () => new MyApp(),
|
||||||
|
'parameters': const [const []],
|
||||||
|
'annotations': const [
|
||||||
|
const ng2.Component(selector: 'my-app'),
|
||||||
|
const ng2.View(template: 'MyApp {{name}}')
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"MyApp":{
|
||||||
|
"id":"MyApp",
|
||||||
|
"selector":"my-app",
|
||||||
|
"compileChildren":true,
|
||||||
|
"host":{},
|
||||||
|
"properties":[],
|
||||||
|
"readAttributes":[],
|
||||||
|
"type":1,
|
||||||
|
"version":1
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue