Skip to content

Commit c8ebd11

Browse files
author
Tim Blasi
committed
feat(dart/transform): Generate DirectiveMetadata for exports
For all files that export another library, include `DirectiveMetadata` for the exported library in that file's associated `ng_meta.json` file.
1 parent 6651aa1 commit c8ebd11

File tree

14 files changed

+260
-62
lines changed

14 files changed

+260
-62
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class _DirectiveMetadataVisitor extends Object
4040
}
4141
meta = new DirectiveMetadata(
4242
type: directiveType,
43-
compileChildren: false,
43+
compileChildren: true,
4444
properties: {},
4545
hostListeners: {},
4646
hostProperties: {},

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ DartFormatter _formatter = null;
88

99
void init(DartFormatter formatter) {
1010
if (_formatter != null) {
11-
logger.warning('Formatter is being overwritten.');
11+
logger.info('Formatter is being overwritten.');
1212
}
1313
_formatter = formatter;
1414
}

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

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,32 @@ const SETUP_METHOD_NAME = 'initReflector';
44
const REFLECTOR_VAR_NAME = 'reflector';
55
const TRANSFORM_DYNAMIC_MODE = 'transform_dynamic';
66
const DEPS_EXTENSION = '.ng_deps.dart';
7-
const META_EXTENSION = '.ng_meta.dart';
7+
const META_EXTENSION = '.ng_meta.json';
88
const REFLECTION_CAPABILITIES_NAME = 'ReflectionCapabilities';
99
const REGISTER_TYPE_METHOD_NAME = 'registerType';
1010
const REGISTER_GETTERS_METHOD_NAME = 'registerGetters';
1111
const REGISTER_SETTERS_METHOD_NAME = 'registerSetters';
1212
const REGISTER_METHODS_METHOD_NAME = 'registerMethods';
13+
14+
/// Returns `uri` with its extension updated to [META_EXTENSION].
15+
String toMetaExtension(String uri) =>
16+
_toExtension(uri, const [DEPS_EXTENSION, '.dart'], META_EXTENSION);
17+
18+
/// Returns `uri` with its extension updated to [DEPS_EXTENSION].
19+
String toDepsExtension(String uri) =>
20+
_toExtension(uri, const [META_EXTENSION, '.dart'], DEPS_EXTENSION);
21+
22+
/// Returns `uri` with its extension updated to `toExtension` if its
23+
/// extension is currently in `fromExtension`.
24+
String _toExtension(
25+
String uri, Iterable<String> fromExtensions, String toExtension) {
26+
if (uri == null) return null;
27+
if (uri.endsWith(toExtension)) return uri;
28+
for (var extension in fromExtensions) {
29+
if (uri.endsWith(extension)) {
30+
return '${uri.substring(0, uri.length-extension.length)}'
31+
'$toExtension';
32+
}
33+
}
34+
return uri;
35+
}

modules/angular2/src/transform/directive_metadata_extractor/extractor.dart

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,59 @@ library angular2.transform.directive_metadata_extractor.extractor;
22

33
import 'dart:async';
44

5+
import 'package:analyzer/analyzer.dart';
56
import 'package:angular2/src/render/api.dart';
67
import 'package:angular2/src/transform/common/asset_reader.dart';
78
import 'package:angular2/src/transform/common/directive_metadata_reader.dart';
9+
import 'package:angular2/src/transform/common/logging.dart';
10+
import 'package:angular2/src/transform/common/names.dart';
811
import 'package:angular2/src/transform/common/parser.dart';
912
import 'package:barback/barback.dart';
13+
import 'package:code_transformers/assets.dart';
1014

1115
/// Returns a map from a class name (that is, its `Identifier` stringified)
12-
/// to its [DirectiveMetadata].
13-
/// Will return `null` if there are no `Directive`-annotated classes in
16+
/// to [DirectiveMetadata] for all `Directive`-annotated classes visible
17+
/// in a file importing `entryPoint`. That is, this includes all
18+
/// `Directive` annotated classes in `entryPoint` and any assets which it
19+
/// `export`s.
20+
/// Returns `null` if there are no `Directive`-annotated classes in
1421
/// `entryPoint`.
1522
Future<Map<String, DirectiveMetadata>> extractDirectiveMetadata(
1623
AssetReader reader, AssetId entryPoint) async {
17-
var parser = new Parser(reader);
18-
NgDeps ngDeps = await parser.parse(entryPoint);
24+
return _extractDirectiveMetadataRecursive(
25+
reader, new Parser(reader), entryPoint);
26+
}
27+
28+
var _nullFuture = new Future.value(null);
29+
30+
Future<Map<String, DirectiveMetadata>> _extractDirectiveMetadataRecursive(
31+
AssetReader reader, Parser parser, AssetId entryPoint) async {
32+
if (!(await reader.hasInput(entryPoint))) return null;
33+
34+
var ngDeps = await parser.parse(entryPoint);
35+
var baseMap = _metadataMapFromNgDeps(ngDeps);
36+
37+
return Future.wait(ngDeps.exports.map((export) {
38+
var uri = stringLiteralToString(export.uri);
39+
if (uri.startsWith('dart:')) return _nullFuture;
40+
41+
uri = toDepsExtension(uri);
42+
var assetId = uriToAssetId(entryPoint, uri, logger, null /* span */);
43+
if (assetId == entryPoint) return _nullFuture;
44+
return _extractDirectiveMetadataRecursive(reader, parser, assetId)
45+
.then((exportMap) {
46+
if (exportMap != null) {
47+
if (baseMap == null) {
48+
baseMap = exportMap;
49+
} else {
50+
baseMap.addAll(exportMap);
51+
}
52+
}
53+
});
54+
})).then((_) => baseMap);
55+
}
56+
57+
Map<String, DirectiveMetadata> _metadataMapFromNgDeps(NgDeps ngDeps) {
1958
if (ngDeps == null || ngDeps.registeredTypes.isEmpty) return null;
2059
var retVal = <String, DirectiveMetadata>{};
2160
ngDeps.registeredTypes.forEach((rType) {

modules/angular2/src/transform/directive_metadata_extractor/transformer.dart

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class DirectiveMetadataExtractor extends Transformer {
3636
jsonMap[k] = directiveMetadataToMap(v);
3737
});
3838
transform.addOutput(new Asset.fromString(
39-
_outputAssetId(fromAssetId), '// ${JSON.encode(jsonMap)}'));
39+
_outputAssetId(fromAssetId), JSON.encode(jsonMap)));
4040
}
4141
} catch (ex, stackTrace) {
4242
log.logger.error('Extracting ng metadata failed.\n'
@@ -49,8 +49,5 @@ class DirectiveMetadataExtractor extends Transformer {
4949

5050
AssetId _outputAssetId(AssetId inputAssetId) {
5151
assert(inputAssetId.path.endsWith(DEPS_EXTENSION));
52-
var pathIn = inputAssetId.path;
53-
return new AssetId(inputAssetId.package,
54-
'${pathIn.substring(0, pathIn.length - DEPS_EXTENSION.length)}'
55-
'${META_EXTENSION}');
52+
return new AssetId(inputAssetId.package, toMetaExtension(inputAssetId.path));
5653
}

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

Lines changed: 93 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ library angular2.test.transform.directive_metadata_extractor.all_tests;
22

33
import 'dart:async';
44
import 'package:angular2/src/render/api.dart';
5+
import 'package:angular2/src/render/dom/convert.dart';
56
import 'package:angular2/src/transform/common/directive_metadata_reader.dart';
67
import 'package:angular2/src/transform/common/logging.dart';
78
import 'package:angular2/src/transform/common/parser.dart';
9+
import 'package:angular2/src/transform/directive_metadata_extractor/'
10+
'extractor.dart';
811
import 'package:barback/barback.dart';
912
import 'package:dart_style/dart_style.dart';
1013
import 'package:guinness/guinness.dart';
@@ -24,50 +27,101 @@ void allTests() {
2427
return readDirectiveMetadata(ngDeps.registeredTypes.first);
2528
}
2629

27-
it('should parse selectors', () async {
28-
var metadata = await readMetadata(
29-
'directive_metadata_extractor/directive_metadata_files/selector.ng_deps.dart');
30-
expect(metadata.selector).toEqual('hello-app');
31-
});
30+
describe('readMetadata', () {
31+
it('should parse selectors', () async {
32+
var metadata = await readMetadata(
33+
'directive_metadata_extractor/directive_metadata_files/'
34+
'selector.ng_deps.dart');
35+
expect(metadata.selector).toEqual('hello-app');
36+
});
3237

33-
it('should parse compile children values', () async {
34-
var metadata = await readMetadata('directive_metadata_extractor/'
35-
'directive_metadata_files/compile_children.ng_deps.dart');
36-
expect(metadata.compileChildren).toBeTrue();
38+
it('should parse compile children values', () async {
39+
var ngDeps = await parser.parse(new AssetId('a',
40+
'directive_metadata_extractor/'
41+
'directive_metadata_files/compile_children.ng_deps.dart'));
42+
var it = ngDeps.registeredTypes.iterator;
3743

38-
metadata = await readMetadata(
39-
'directive_metadata_extractor/directive_metadata_files/selector.ng_deps.dart');
40-
expect(metadata.compileChildren).toBeFalse();
41-
});
44+
// Unset value defaults to `true`.
45+
it.moveNext();
46+
expect('${it.current.typeName}').toEqual('UnsetComp');
47+
var unsetComp = readDirectiveMetadata(it.current);
48+
expect(unsetComp.compileChildren).toBeTrue();
4249

43-
it('should parse properties.', () async {
44-
var metadata = await readMetadata('directive_metadata_extractor/'
45-
'directive_metadata_files/properties.ng_deps.dart');
46-
expect(metadata.properties).toBeNotNull();
47-
expect(metadata.properties.length).toBe(2);
48-
expect(metadata.properties).toContain('key1');
49-
expect(metadata.properties['key1']).toEqual('val1');
50-
expect(metadata.properties).toContain('key2');
51-
expect(metadata.properties['key2']).toEqual('val2');
52-
});
50+
it.moveNext();
51+
expect('${it.current.typeName}').toEqual('FalseComp');
52+
var falseComp = readDirectiveMetadata(it.current);
53+
expect(falseComp.compileChildren).toBeFalse();
54+
55+
it.moveNext();
56+
expect('${it.current.typeName}').toEqual('TrueComp');
57+
var trueComp = readDirectiveMetadata(it.current);
58+
expect(trueComp.compileChildren).toBeTrue();
59+
});
60+
61+
it('should parse properties.', () async {
62+
var metadata = await readMetadata('directive_metadata_extractor/'
63+
'directive_metadata_files/properties.ng_deps.dart');
64+
expect(metadata.properties).toBeNotNull();
65+
expect(metadata.properties.length).toBe(2);
66+
expect(metadata.properties).toContain('key1');
67+
expect(metadata.properties['key1']).toEqual('val1');
68+
expect(metadata.properties).toContain('key2');
69+
expect(metadata.properties['key2']).toEqual('val2');
70+
});
71+
72+
it('should parse host listeners.', () async {
73+
var metadata = await readMetadata('directive_metadata_extractor/'
74+
'directive_metadata_files/host_listeners.ng_deps.dart');
75+
expect(metadata.hostListeners).toBeNotNull();
76+
expect(metadata.hostListeners.length).toBe(2);
77+
expect(metadata.hostListeners).toContain('change');
78+
expect(metadata.hostListeners['change']).toEqual('onChange(\$event)');
79+
expect(metadata.hostListeners).toContain('keyDown');
80+
expect(metadata.hostListeners['keyDown']).toEqual('onKeyDown(\$event)');
81+
});
5382

54-
it('should parse host listeners.', () async {
55-
var metadata = await readMetadata('directive_metadata_extractor/'
56-
'directive_metadata_files/host_listeners.ng_deps.dart');
57-
expect(metadata.hostListeners).toBeNotNull();
58-
expect(metadata.hostListeners.length).toBe(2);
59-
expect(metadata.hostListeners).toContain('change');
60-
expect(metadata.hostListeners['change']).toEqual('onChange(\$event)');
61-
expect(metadata.hostListeners).toContain('keyDown');
62-
expect(metadata.hostListeners['keyDown']).toEqual('onKeyDown(\$event)');
83+
it('should fail when a class is annotated with multiple Directives.',
84+
() async {
85+
var ngDeps = await parser.parse(new AssetId('a',
86+
'directive_metadata_extractor/'
87+
'directive_metadata_files/too_many_directives.ng_deps.dart'));
88+
expect(() => readDirectiveMetadata(ngDeps.registeredTypes.first))
89+
.toThrowWith(anInstanceOf: PrintLoggerError);
90+
});
6391
});
6492

65-
it('should fail when a class is annotated with multiple Directives.',
66-
() async {
67-
var ngDeps = await parser.parse(new AssetId('a',
68-
'directive_metadata_extractor/'
69-
'directive_metadata_files/too_many_directives.ng_deps.dart'));
70-
expect(() => readDirectiveMetadata(ngDeps.registeredTypes.first))
71-
.toThrowWith(anInstanceOf: PrintLoggerError);
93+
describe('extractMetadata', () {
94+
it('should generate `DirectiveMetadata` from .ng_deps.dart files.',
95+
() async {
96+
var extracted = await extractDirectiveMetadata(reader, new AssetId(
97+
'a', 'directive_metadata_extractor/simple_files/foo.ng_deps.dart'));
98+
expect(extracted).toContain('FooComponent');
99+
100+
var extractedMeta = extracted['FooComponent'];
101+
expect(extractedMeta.selector).toEqual('[foo]');
102+
});
103+
104+
it('should include `DirectiveMetadata` from exported files.', () async {
105+
var extracted = await extractDirectiveMetadata(reader, new AssetId(
106+
'a', 'directive_metadata_extractor/export_files/foo.ng_deps.dart'));
107+
expect(extracted).toContain('FooComponent');
108+
expect(extracted).toContain('BarComponent');
109+
110+
expect(extracted['FooComponent'].selector).toEqual('[foo]');
111+
expect(extracted['BarComponent'].selector).toEqual('[bar]');
112+
});
113+
114+
it('should include `DirectiveMetadata` recursively from exported files.',
115+
() async {
116+
var extracted = await extractDirectiveMetadata(reader, new AssetId('a',
117+
'directive_metadata_extractor/recursive_export_files/foo.ng_deps.dart'));
118+
expect(extracted).toContain('FooComponent');
119+
expect(extracted).toContain('BarComponent');
120+
expect(extracted).toContain('BazComponent');
121+
122+
expect(extracted['FooComponent'].selector).toEqual('[foo]');
123+
expect(extracted['BarComponent'].selector).toEqual('[bar]');
124+
expect(extracted['BazComponent'].selector).toEqual('[baz]');
125+
});
72126
});
73127
}

modules/angular2/test/transform/directive_metadata_extractor/directive_metadata_files/compile_children.ng_deps.dart

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,18 @@ void initReflector(reflector) {
88
if (_visited) return;
99
_visited = true;
1010
reflector
11-
..registerType(HelloCmp, {
12-
'factory': () => new HelloCmp(),
11+
..registerType(UnsetComp, {
12+
'factory': () => new UnsetComp(),
13+
'parameters': const [const []],
14+
'annotations': const [const Directive()]
15+
})
16+
..registerType(FalseComp, {
17+
'factory': () => new FalseComp(),
18+
'parameters': const [const []],
19+
'annotations': const [const Directive(compileChildren: false)]
20+
})
21+
..registerType(TrueComp, {
22+
'factory': () => new TrueComp(),
1323
'parameters': const [const []],
1424
'annotations': const [const Directive(compileChildren: true)]
1525
});
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
library foo.ng_deps.dart;
22

3-
import 'foo.dart';
3+
import 'bar.dart';
44
import 'package:angular2/src/core/annotations/annotations.dart';
55

66
var _visited = false;
77
void initReflector(reflector) {
88
if (_visited) return;
99
_visited = true;
1010
reflector
11-
..registerType(DependencyComponent, {
12-
'factory': () => new DependencyComponent(),
11+
..registerType(BarComponent, {
12+
'factory': () => new BarComponent(),
1313
'parameters': const [],
14-
'annotations': const [const Component(selector: '[salad]')]
14+
'annotations': const [const Component(selector: '[bar]')]
1515
});
1616
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
library foo.ng_deps.dart;
2+
3+
import 'foo.dart';
4+
import 'package:angular2/src/core/annotations/annotations.dart';
5+
6+
export 'bar.dart';
7+
import 'bar.ng_deps.dart' as i0;
8+
9+
var _visited = false;
10+
void initReflector(reflector) {
11+
if (_visited) return;
12+
_visited = true;
13+
reflector
14+
..registerType(FooComponent, {
15+
'factory': () => new FooComponent(),
16+
'parameters': const [],
17+
'annotations': const [const Component(selector: '[foo]')]
18+
});
19+
i0.initReflector(reflector);
20+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
library foo.ng_deps.dart;
2+
3+
import 'bar.dart';
4+
import 'package:angular2/src/core/annotations/annotations.dart';
5+
6+
export 'baz.dart';
7+
import 'baz.ng_deps.dart' as i0;
8+
9+
var _visited = false;
10+
void initReflector(reflector) {
11+
if (_visited) return;
12+
_visited = true;
13+
reflector
14+
..registerType(BarComponent, {
15+
'factory': () => new BarComponent(),
16+
'parameters': const [],
17+
'annotations': const [const Component(selector: '[bar]')]
18+
});
19+
i0.initReflector(reflector);
20+
}

0 commit comments

Comments
 (0)