diff --git a/modules/angular2/src/transform/template_compiler/directive_metadata_reader.dart b/modules/angular2/src/transform/template_compiler/directive_metadata_reader.dart index 5183d5c71b..a7316e9b0d 100644 --- a/modules/angular2/src/transform/template_compiler/directive_metadata_reader.dart +++ b/modules/angular2/src/transform/template_compiler/directive_metadata_reader.dart @@ -6,10 +6,10 @@ import 'package:angular2/src/transform/common/logging.dart'; import 'package:angular2/src/transform/common/parser.dart'; /// Reads [DirectiveMetadata] from the `attributes` of `t`. -List readDirectiveMetadata(RegisteredType t) { +DirectiveMetadata readDirectiveMetadata(RegisteredType t) { var visitor = new _DirectiveMetadataVisitor(); t.annotations.accept(visitor); - return visitor.directiveMetadata; + return visitor.meta; } num _getDirectiveType(String annotationName) { @@ -30,21 +30,23 @@ num _getDirectiveType(String annotationName) { /// [RegisterType] object and pulling out [DirectiveMetadata]. class _DirectiveMetadataVisitor extends Object with RecursiveAstVisitor { - DirectiveMetadata current; - final List directiveMetadata = []; + DirectiveMetadata meta; @override Object visitInstanceCreationExpression(InstanceCreationExpression node) { var directiveType = _getDirectiveType('${node.constructorName.type.name}'); if (directiveType >= 0) { - current = new DirectiveMetadata( + if (meta != null) { + logger.error('Only one Directive is allowed per class. ' + 'Found "$node" but already processed "$meta".'); + } + meta = new DirectiveMetadata( type: directiveType, compileChildren: false, properties: {}, hostListeners: {}, setters: [], readAttributes: []); - directiveMetadata.add(current); super.visitInstanceCreationExpression(node); } // Annotation we do not recognize - no need to visit. @@ -91,7 +93,7 @@ class _DirectiveMetadataVisitor extends Object } void _populateSelector(Expression selectorValue) { - current.selector = _expressionToString(selectorValue, 'Directive#selector'); + meta.selector = _expressionToString(selectorValue, 'Directive#selector'); } void _populateCompileChildren(Expression compileChildrenValue) { @@ -102,7 +104,7 @@ class _DirectiveMetadataVisitor extends Object ' Source: ${compileChildrenValue}'); return; } - current.compileChildren = (compileChildrenValue as BooleanLiteral).value; + meta.compileChildren = (compileChildrenValue as BooleanLiteral).value; } void _populateProperties(Expression propertiesValue) { @@ -115,7 +117,7 @@ class _DirectiveMetadataVisitor extends Object for (MapLiteralEntry entry in (propertiesValue as MapLiteral).entries) { var sKey = _expressionToString(entry.key, 'Directive#properties keys'); var sVal = _expressionToString(entry.value, 'Direcive#properties values'); - current.properties[sKey] = sVal; + meta.properties[sKey] = sVal; } } @@ -130,7 +132,7 @@ class _DirectiveMetadataVisitor extends Object var sKey = _expressionToString(entry.key, 'Directive#hostListeners keys'); var sVal = _expressionToString(entry.value, 'Directive#hostListeners values'); - current.hostListeners[sKey] = sVal; + meta.hostListeners[sKey] = sVal; } } } diff --git a/modules/angular2/test/transform/template_compiler/all_tests.dart b/modules/angular2/test/transform/template_compiler/all_tests.dart index c6166f501c..8505c8572a 100644 --- a/modules/angular2/test/transform/template_compiler/all_tests.dart +++ b/modules/angular2/test/transform/template_compiler/all_tests.dart @@ -5,6 +5,7 @@ import 'package:barback/barback.dart'; import 'package:angular2/src/dom/html_adapter.dart'; import 'package:angular2/src/render/api.dart'; import 'package:angular2/src/transform/common/asset_reader.dart'; +import 'package:angular2/src/transform/common/logging.dart'; import 'package:angular2/src/transform/common/parser.dart'; import 'package:angular2/src/transform/template_compiler/directive_metadata_reader.dart'; import 'package:angular2/src/transform/template_compiler/generator.dart'; @@ -20,6 +21,8 @@ void allTests() { AssetReader reader = new TestAssetReader(); var parser = new Parser(reader); + beforeEach(() => setLogger(new PrintLogger())); + it('should parse simple expressions in inline templates.', () async { var inputPath = 'template_compiler/inline_expression_files/hello.ng_deps.dart'; @@ -54,31 +57,29 @@ void allTests() { }); describe('DirectiveMetadataReader', () { - Future readSingleMetadata(inputPath) async { + Future readMetadata(inputPath) async { var ngDeps = await parser.parse(new AssetId('a', inputPath)); - var metadata = readDirectiveMetadata(ngDeps.registeredTypes.first); - expect(metadata.length).toEqual(1); - return metadata.first; + return readDirectiveMetadata(ngDeps.registeredTypes.first); } it('should parse selectors', () async { - var metadata = await readSingleMetadata( + var metadata = await readMetadata( 'template_compiler/directive_metadata_files/selector.ng_deps.dart'); expect(metadata.selector).toEqual('hello-app'); }); it('should parse compile children values', () async { - var metadata = await readSingleMetadata('template_compiler/' + var metadata = await readMetadata('template_compiler/' 'directive_metadata_files/compile_children.ng_deps.dart'); expect(metadata.compileChildren).toBeTrue(); - metadata = await readSingleMetadata( + metadata = await readMetadata( 'template_compiler/directive_metadata_files/selector.ng_deps.dart'); expect(metadata.compileChildren).toBeFalse(); }); it('should parse properties.', () async { - var metadata = await readSingleMetadata('template_compiler/' + var metadata = await readMetadata('template_compiler/' 'directive_metadata_files/properties.ng_deps.dart'); expect(metadata.properties).toBeNotNull(); expect(metadata.properties.length).toBe(2); @@ -89,7 +90,7 @@ void allTests() { }); it('should parse host listeners.', () async { - var metadata = await readSingleMetadata('template_compiler/' + var metadata = await readMetadata('template_compiler/' 'directive_metadata_files/host_listeners.ng_deps.dart'); expect(metadata.hostListeners).toBeNotNull(); expect(metadata.hostListeners.length).toBe(2); @@ -98,6 +99,14 @@ void allTests() { expect(metadata.hostListeners).toContain('keyDown'); expect(metadata.hostListeners['keyDown']).toEqual('onKeyDown(\$event)'); }); + + it('should fail when a class is annotated with multiple Directives.', + () async { + var ngDeps = await parser.parse(new AssetId('a', 'template_compiler/' + 'directive_metadata_files/too_many_directives.ng_deps.dart')); + expect(() => readDirectiveMetadata(ngDeps.registeredTypes.first)) + .toThrowWith(anInstanceOf: PrintLoggerError); + }); }); } diff --git a/modules/angular2/test/transform/template_compiler/directive_metadata_files/too_many_directives.ng_deps.dart b/modules/angular2/test/transform/template_compiler/directive_metadata_files/too_many_directives.ng_deps.dart new file mode 100644 index 0000000000..89c06f55a4 --- /dev/null +++ b/modules/angular2/test/transform/template_compiler/directive_metadata_files/too_many_directives.ng_deps.dart @@ -0,0 +1,20 @@ +library examples.hello_world.index_common_dart.ng_deps.dart; + +import 'hello.dart'; +import 'package:angular2/angular2.dart' + show bootstrap, Component, Decorator, View, NgElement; + +bool _visited = false; +void initReflector(reflector) { + if (_visited) return; + _visited = true; + reflector + ..registerType(HelloCmp, { + 'factory': () => new HelloCmp(), + 'parameters': const [const []], + 'annotations': const [ + const Component(selector: 'hello-app'), + const Component(selector: 'goodbye-app') + ] + }); +}