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:
parent
af9f916a9c
commit
e7d65ad96f
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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()));
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
Loading…
Reference in New Issue