diff --git a/modules_dart/transform/lib/src/transform/directive_metadata_extractor/extractor.dart b/modules_dart/transform/lib/src/transform/directive_metadata_extractor/extractor.dart index 667ea97395..1dc047ba54 100644 --- a/modules_dart/transform/lib/src/transform/directive_metadata_extractor/extractor.dart +++ b/modules_dart/transform/lib/src/transform/directive_metadata_extractor/extractor.dart @@ -22,13 +22,16 @@ import 'package:code_transformers/assets.dart'; /// [NgMeta] if there are no `Directive`-annotated classes in `entryPoint`. Future extractDirectiveMetadata( AssetReader reader, AssetId entryPoint) { - return _extractDirectiveMetadataRecursive(reader, entryPoint); + return _extractDirectiveMetadataRecursive( + reader, entryPoint, new Set()); } -var _nullFuture = new Future.value(null); +final _nullFuture = new Future.value(null); Future _extractDirectiveMetadataRecursive( - AssetReader reader, AssetId entryPoint) async { + AssetReader reader, AssetId entryPoint, Set seen) async { + if (seen.contains(entryPoint)) return _nullFuture; + seen.add(entryPoint); var ngMeta = new NgMeta.empty(); if (!(await reader.hasInput(entryPoint))) return ngMeta; @@ -51,8 +54,12 @@ Future _extractDirectiveMetadataRecursive( var assetId = uriToAssetId(entryPoint, uri, logger, null /* span */, errorOnAbsolute: false); if (assetId == entryPoint) return _nullFuture; - return _extractDirectiveMetadataRecursive(reader, assetId) - .then(ngMeta.addAll); + return _extractDirectiveMetadataRecursive(reader, assetId, seen) + .then((exportedNgMeta) { + if (exportedNgMeta != null) { + ngMeta.addAll(exportedNgMeta); + } + }); })); return ngMeta; } diff --git a/modules_dart/transform/test/transform/directive_metadata_extractor/all_tests.dart b/modules_dart/transform/test/transform/directive_metadata_extractor/all_tests.dart index 047c853829..ed74972c66 100644 --- a/modules_dart/transform/test/transform/directive_metadata_extractor/all_tests.dart +++ b/modules_dart/transform/test/transform/directive_metadata_extractor/all_tests.dart @@ -193,6 +193,20 @@ void allTests() { expect(extracted.types['BazComponent'].selector).toEqual('[baz]'); }); + it('should handle `DirectiveMetadata` export cycles gracefully.', () async { + var extracted = await extractDirectiveMetadata( + reader, + new AssetId('a', + 'directive_metadata_extractor/export_cycle_files/baz.ng_deps.dart')); + expect(extracted.types).toContain('FooComponent'); + expect(extracted.types).toContain('BarComponent'); + expect(extracted.types).toContain('BazComponent'); + + expect(extracted.types['FooComponent'].selector).toEqual('[foo]'); + expect(extracted.types['BarComponent'].selector).toEqual('[bar]'); + expect(extracted.types['BazComponent'].selector).toEqual('[baz]'); + }); + it( 'should include `DirectiveMetadata` from exported files ' 'expressed as absolute uris', () async { diff --git a/modules_dart/transform/test/transform/directive_metadata_extractor/export_cycle_files/bar.ng_deps.dart b/modules_dart/transform/test/transform/directive_metadata_extractor/export_cycle_files/bar.ng_deps.dart new file mode 100644 index 0000000000..0381a810ef --- /dev/null +++ b/modules_dart/transform/test/transform/directive_metadata_extractor/export_cycle_files/bar.ng_deps.dart @@ -0,0 +1,19 @@ +library foo.ng_deps.dart; + +import 'bar.dart'; +import 'package:angular2/src/core/metadata.dart'; + +export 'baz.dart'; +import 'baz.ng_deps.dart' as i0; + +var _visited = false; +void initReflector(reflector) { + if (_visited) return; + _visited = true; + reflector + ..registerType( + BarComponent, + new ReflectionInfo(const [const Component(selector: '[bar]')], const [], + () => new BarComponent())); + i0.initReflector(reflector); +} diff --git a/modules_dart/transform/test/transform/directive_metadata_extractor/export_cycle_files/baz.ng_deps.dart b/modules_dart/transform/test/transform/directive_metadata_extractor/export_cycle_files/baz.ng_deps.dart new file mode 100644 index 0000000000..58fee37db4 --- /dev/null +++ b/modules_dart/transform/test/transform/directive_metadata_extractor/export_cycle_files/baz.ng_deps.dart @@ -0,0 +1,17 @@ +library foo.ng_deps.dart; + +import 'baz.dart'; +import 'package:angular2/src/core/metadata.dart'; + +export 'foo.dart'; + +var _visited = false; +void initReflector(reflector) { + if (_visited) return; + _visited = true; + reflector + ..registerType( + BazComponent, + new ReflectionInfo(const [const Component(selector: '[baz]')], const [], + () => new BazComponent())); +} diff --git a/modules_dart/transform/test/transform/directive_metadata_extractor/export_cycle_files/foo.ng_deps.dart b/modules_dart/transform/test/transform/directive_metadata_extractor/export_cycle_files/foo.ng_deps.dart new file mode 100644 index 0000000000..542f008d16 --- /dev/null +++ b/modules_dart/transform/test/transform/directive_metadata_extractor/export_cycle_files/foo.ng_deps.dart @@ -0,0 +1,19 @@ +library foo.ng_deps.dart; + +import 'foo.dart'; +import 'package:angular2/src/core/metadata.dart'; + +export 'bar.dart'; +import 'bar.ng_deps.dart' as i0; + +var _visited = false; +void initReflector(reflector) { + if (_visited) return; + _visited = true; + reflector + ..registerType( + FooComponent, + new ReflectionInfo(const [const Component(selector: '[foo]')], const [], + () => new FooComponent())); + i0.initReflector(reflector); +}