fix(dart/transform): Handle export cycles

Currently, an export cycle in dart inputs will cause the transformer to
hang indefinitely on the `DirectiveMetadataExtractor` step.

Closes #4370
This commit is contained in:
Tim Blasi 2015-09-25 13:33:25 -07:00
parent af9f916a9c
commit e7d65ad96f
5 changed files with 81 additions and 5 deletions

View File

@ -22,13 +22,16 @@ import 'package:code_transformers/assets.dart';
/// [NgMeta] if there are no `Directive`-annotated classes in `entryPoint`. /// [NgMeta] if there are no `Directive`-annotated classes in `entryPoint`.
Future<NgMeta> extractDirectiveMetadata( Future<NgMeta> extractDirectiveMetadata(
AssetReader reader, AssetId entryPoint) { AssetReader reader, AssetId entryPoint) {
return _extractDirectiveMetadataRecursive(reader, entryPoint); return _extractDirectiveMetadataRecursive(
reader, entryPoint, new Set<AssetId>());
} }
var _nullFuture = new Future.value(null); final _nullFuture = new Future.value(null);
Future<NgMeta> _extractDirectiveMetadataRecursive( Future<NgMeta> _extractDirectiveMetadataRecursive(
AssetReader reader, AssetId entryPoint) async { AssetReader reader, AssetId entryPoint, Set<AssetId> seen) async {
if (seen.contains(entryPoint)) return _nullFuture;
seen.add(entryPoint);
var ngMeta = new NgMeta.empty(); var ngMeta = new NgMeta.empty();
if (!(await reader.hasInput(entryPoint))) return ngMeta; if (!(await reader.hasInput(entryPoint))) return ngMeta;
@ -51,8 +54,12 @@ Future<NgMeta> _extractDirectiveMetadataRecursive(
var assetId = uriToAssetId(entryPoint, uri, logger, null /* span */, var assetId = uriToAssetId(entryPoint, uri, logger, null /* span */,
errorOnAbsolute: false); errorOnAbsolute: false);
if (assetId == entryPoint) return _nullFuture; if (assetId == entryPoint) return _nullFuture;
return _extractDirectiveMetadataRecursive(reader, assetId) return _extractDirectiveMetadataRecursive(reader, assetId, seen)
.then(ngMeta.addAll); .then((exportedNgMeta) {
if (exportedNgMeta != null) {
ngMeta.addAll(exportedNgMeta);
}
});
})); }));
return ngMeta; return ngMeta;
} }

View File

@ -193,6 +193,20 @@ void allTests() {
expect(extracted.types['BazComponent'].selector).toEqual('[baz]'); 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( it(
'should include `DirectiveMetadata` from exported files ' 'should include `DirectiveMetadata` from exported files '
'expressed as absolute uris', () async { 'expressed as absolute uris', () async {

View File

@ -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);
}

View File

@ -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()));
}

View File

@ -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);
}