Skip to content

Commit 9e1158d

Browse files
committed
fix(transformer): Support prefixed annotations in the transformer.
closes #2754
1 parent 569766f commit 9e1158d

File tree

7 files changed

+202
-7
lines changed

7 files changed

+202
-7
lines changed

modules/angular2/src/transform/common/annotation_matcher.dart

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -127,17 +127,37 @@ class AnnotationMatcher {
127127
// Checks if an [Annotation] matches an [AnnotationDescriptor].
128128
static bool _matchAnnotation(
129129
Annotation annotation, AnnotationDescriptor descriptor, AssetId assetId) {
130-
if (annotation.name.name != descriptor.name) return false;
130+
String name;
131+
Identifier prefix;
132+
if (annotation.name is PrefixedIdentifier) {
133+
// TODO(jakemac): Shouldn't really need a cast here, remove once
134+
// https://github.com/dart-lang/sdk/issues/23798 is fixed.
135+
var prefixedName = annotation.name as PrefixedIdentifier;
136+
name = prefixedName.identifier.name;
137+
prefix = prefixedName.prefix;
138+
} else {
139+
name = annotation.name.name;
140+
}
141+
if (name != descriptor.name) return false;
131142
return (annotation.root as CompilationUnit).directives
132143
.where((d) => d is ImportDirective)
133144
.any((ImportDirective i) {
145+
var importMatch = false;
134146
var uriString = i.uri.stringValue;
135-
if (uriString == descriptor.import) return true;
136-
if (uriString.startsWith('package:') || uriString.startsWith('dart:')) {
147+
if (uriString == descriptor.import) {
148+
importMatch = true;
149+
} else if (uriString.startsWith('package:') ||
150+
uriString.startsWith('dart:')) {
137151
return false;
152+
} else {
153+
importMatch = descriptor.assetId ==
154+
uriToAssetId(assetId, uriString, logger, null);
138155
}
139-
return descriptor.assetId ==
140-
uriToAssetId(assetId, uriString, logger, null);
156+
157+
if (!importMatch) return false;
158+
if (prefix == null) return i.prefix == null;
159+
if (i.prefix == null) return false;
160+
return prefix.name == i.prefix.name;
141161
});
142162
}
143163
}

modules/angular2/src/transform/template_compiler/view_definition_creator.dart

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,13 @@ class ViewDefinitionEntry {
3636
String _getComponentId(AssetId assetId, String className) => '$className';
3737

3838
// TODO(kegluenq): Improve this test.
39-
bool _isViewAnnotation(InstanceCreationExpression node) =>
40-
'${node.constructorName.type}' == 'View';
39+
bool _isViewAnnotation(InstanceCreationExpression node) {
40+
var constructorName = node.constructorName.type.name;
41+
if (constructorName is PrefixedIdentifier) {
42+
constructorName = constructorName.identifier;
43+
}
44+
return constructorName.name == 'View';
45+
}
4146

4247
/// Creates [ViewDefinition] objects for all `View` `Directive`s defined in
4348
/// `entryPoint`.
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
library angular2.test.transform.common.annotation_matcher_test;
2+
3+
import 'dart:async';
4+
import 'package:analyzer/analyzer.dart';
5+
import 'package:angular2/src/transform/common/annotation_matcher.dart';
6+
import 'package:barback/barback.dart' show AssetId;
7+
import 'package:guinness/guinness.dart';
8+
9+
main() {
10+
allTests();
11+
}
12+
13+
var simpleAst = parseCompilationUnit('''
14+
import 'package:test/test.dart';
15+
16+
@Test()
17+
var foo;
18+
''');
19+
20+
var namespacedAst = parseCompilationUnit('''
21+
import 'package:test/test.dart' as test;
22+
23+
@test.Test()
24+
var foo;
25+
''');
26+
27+
var relativePathAst = parseCompilationUnit('''
28+
import 'test.dart';
29+
30+
@Test()
31+
var foo;
32+
''');
33+
34+
var namespacedRelativePathAst = parseCompilationUnit('''
35+
import 'test.dart' as test;
36+
37+
@test.Test()
38+
var foo;
39+
''');
40+
41+
void allTests() {
42+
it('should be able to match basic annotations.', () {
43+
var matcher = new AnnotationMatcher()
44+
..add(const AnnotationDescriptor('Test', 'package:test/test.dart', null));
45+
var visitor = new MatchRecordingVisitor(matcher);
46+
simpleAst.accept(visitor);
47+
expect(visitor.matches.length).toBe(1);
48+
});
49+
50+
it('should be able to match namespaced annotations.', () {
51+
var matcher = new AnnotationMatcher()
52+
..add(const AnnotationDescriptor('Test', 'package:test/test.dart', null));
53+
var visitor = new MatchRecordingVisitor(matcher);
54+
namespacedAst.accept(visitor);
55+
expect(visitor.matches.length).toBe(1);
56+
});
57+
58+
it('should be able to match relative imports.', () {
59+
var matcher = new AnnotationMatcher()
60+
..add(const AnnotationDescriptor('Test', 'package:test/test.dart', null));
61+
var visitor =
62+
new MatchRecordingVisitor(matcher, new AssetId('test', 'lib/foo.dart'));
63+
relativePathAst.accept(visitor);
64+
expect(visitor.matches.length).toBe(1);
65+
});
66+
67+
it('should be able to match relative imports with a namespace.', () {
68+
var matcher = new AnnotationMatcher()
69+
..add(const AnnotationDescriptor('Test', 'package:test/test.dart', null));
70+
var visitor =
71+
new MatchRecordingVisitor(matcher, new AssetId('test', 'lib/foo.dart'));
72+
namespacedRelativePathAst.accept(visitor);
73+
expect(visitor.matches.length).toBe(1);
74+
});
75+
76+
it('should not match annotations if the import is missing.', () {
77+
var matcher = new AnnotationMatcher()
78+
..add(const AnnotationDescriptor('Test', 'package:test/foo.dart', null));
79+
var visitor = new MatchRecordingVisitor(matcher);
80+
simpleAst.accept(visitor);
81+
expect(visitor.matches.isEmpty).toBeTrue();
82+
});
83+
84+
it('should not match annotations if the name is different.', () {
85+
var matcher = new AnnotationMatcher()
86+
..add(const AnnotationDescriptor('Foo', 'package:test/test.dart', null));
87+
var visitor = new MatchRecordingVisitor(matcher);
88+
simpleAst.accept(visitor);
89+
expect(visitor.matches.isEmpty).toBeTrue();
90+
});
91+
}
92+
93+
class MatchRecordingVisitor extends RecursiveAstVisitor {
94+
final AssetId assetId;
95+
final AnnotationMatcher matcher;
96+
final List<Annotation> matches = [];
97+
98+
MatchRecordingVisitor(this.matcher, [this.assetId]) : super();
99+
100+
@override
101+
void visitAnnotation(Annotation annotation) {
102+
if (matcher.hasMatch(annotation, assetId)) matches.add(annotation);
103+
}
104+
}

modules/angular2/test/transform/template_compiler/all_tests.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import '../common/read_file.dart';
1313

1414
var formatter = new DartFormatter();
1515

16+
main() => allTests();
17+
1618
void allTests() {
1719
Html5LibDomAdapter.makeCurrent();
1820
AssetReader reader = new TestAssetReader();
@@ -93,6 +95,16 @@ void allTests() {
9395
_formatThenExpectEquals(output, expected);
9496
});
9597

98+
it('should parse angular directives with a prefix', () async {
99+
var inputPath =
100+
'template_compiler/with_prefix_files/ng2_prefix.ng_deps.dart';
101+
var expected = readFile(
102+
'template_compiler/with_prefix_files/expected/ng2_prefix.ng_deps.dart');
103+
104+
var output = await process(new AssetId('a', inputPath));
105+
_formatThenExpectEquals(output, expected);
106+
});
107+
96108
it('should create the same output for multiple calls.', () async {
97109
var inputPath =
98110
'template_compiler/inline_expression_files/hello.ng_deps.dart';
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
library angular2.test.transform.template_compiler.with_prefix_files.ng2_prefix.ng_deps.dart;
2+
3+
import 'ng2_prefix.dart';
4+
import 'package:angular2/angular2.dart' as ng2
5+
show bootstrap, Component, Directive, View, NgElement;
6+
7+
var _visited = false;
8+
void initReflector(reflector) {
9+
if (_visited) return;
10+
_visited = true;
11+
reflector
12+
..registerType(MyApp, {
13+
'factory': () => new MyApp(),
14+
'parameters': const [const []],
15+
'annotations': const [
16+
const ng2.Component(selector: 'my-app'),
17+
const ng2.View(template: 'MyApp {{name}}')
18+
]
19+
})
20+
..registerGetters({'name': (o) => o.name})
21+
..registerSetters({'name': (o, v) => o.name = v});
22+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
library angular2.test.transform.template_compiler.with_prefix_files.ng2_prefix.ng_deps.dart;
2+
3+
import 'ng2_prefix.dart';
4+
import 'package:angular2/angular2.dart' as ng2
5+
show bootstrap, Component, Directive, View, NgElement;
6+
7+
var _visited = false;
8+
void initReflector(reflector) {
9+
if (_visited) return;
10+
_visited = true;
11+
reflector
12+
..registerType(MyApp, {
13+
'factory': () => new MyApp(),
14+
'parameters': const [const []],
15+
'annotations': const [
16+
const ng2.Component(selector: 'my-app'),
17+
const ng2.View(template: 'MyApp {{name}}')
18+
]
19+
});
20+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"MyApp":{
3+
"id":"MyApp",
4+
"selector":"my-app",
5+
"compileChildren":true,
6+
"host":{},
7+
"properties":[],
8+
"readAttributes":[],
9+
"type":1,
10+
"version":1
11+
}
12+
}

0 commit comments

Comments
 (0)