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.
This commit is contained in:
parent
6651aa1e1d
commit
c8ebd11d63
|
@ -40,7 +40,7 @@ class _DirectiveMetadataVisitor extends Object
|
|||
}
|
||||
meta = new DirectiveMetadata(
|
||||
type: directiveType,
|
||||
compileChildren: false,
|
||||
compileChildren: true,
|
||||
properties: {},
|
||||
hostListeners: {},
|
||||
hostProperties: {},
|
||||
|
|
|
@ -8,7 +8,7 @@ DartFormatter _formatter = null;
|
|||
|
||||
void init(DartFormatter formatter) {
|
||||
if (_formatter != null) {
|
||||
logger.warning('Formatter is being overwritten.');
|
||||
logger.info('Formatter is being overwritten.');
|
||||
}
|
||||
_formatter = formatter;
|
||||
}
|
||||
|
|
|
@ -4,9 +4,32 @@ const SETUP_METHOD_NAME = 'initReflector';
|
|||
const REFLECTOR_VAR_NAME = 'reflector';
|
||||
const TRANSFORM_DYNAMIC_MODE = 'transform_dynamic';
|
||||
const DEPS_EXTENSION = '.ng_deps.dart';
|
||||
const META_EXTENSION = '.ng_meta.dart';
|
||||
const META_EXTENSION = '.ng_meta.json';
|
||||
const REFLECTION_CAPABILITIES_NAME = 'ReflectionCapabilities';
|
||||
const REGISTER_TYPE_METHOD_NAME = 'registerType';
|
||||
const REGISTER_GETTERS_METHOD_NAME = 'registerGetters';
|
||||
const REGISTER_SETTERS_METHOD_NAME = 'registerSetters';
|
||||
const REGISTER_METHODS_METHOD_NAME = 'registerMethods';
|
||||
|
||||
/// Returns `uri` with its extension updated to [META_EXTENSION].
|
||||
String toMetaExtension(String uri) =>
|
||||
_toExtension(uri, const [DEPS_EXTENSION, '.dart'], META_EXTENSION);
|
||||
|
||||
/// Returns `uri` with its extension updated to [DEPS_EXTENSION].
|
||||
String toDepsExtension(String uri) =>
|
||||
_toExtension(uri, const [META_EXTENSION, '.dart'], DEPS_EXTENSION);
|
||||
|
||||
/// Returns `uri` with its extension updated to `toExtension` if its
|
||||
/// extension is currently in `fromExtension`.
|
||||
String _toExtension(
|
||||
String uri, Iterable<String> fromExtensions, String toExtension) {
|
||||
if (uri == null) return null;
|
||||
if (uri.endsWith(toExtension)) return uri;
|
||||
for (var extension in fromExtensions) {
|
||||
if (uri.endsWith(extension)) {
|
||||
return '${uri.substring(0, uri.length-extension.length)}'
|
||||
'$toExtension';
|
||||
}
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
|
|
@ -2,20 +2,59 @@ library angular2.transform.directive_metadata_extractor.extractor;
|
|||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:analyzer/analyzer.dart';
|
||||
import 'package:angular2/src/render/api.dart';
|
||||
import 'package:angular2/src/transform/common/asset_reader.dart';
|
||||
import 'package:angular2/src/transform/common/directive_metadata_reader.dart';
|
||||
import 'package:angular2/src/transform/common/logging.dart';
|
||||
import 'package:angular2/src/transform/common/names.dart';
|
||||
import 'package:angular2/src/transform/common/parser.dart';
|
||||
import 'package:barback/barback.dart';
|
||||
import 'package:code_transformers/assets.dart';
|
||||
|
||||
/// Returns a map from a class name (that is, its `Identifier` stringified)
|
||||
/// to its [DirectiveMetadata].
|
||||
/// Will return `null` if there are no `Directive`-annotated classes in
|
||||
/// to [DirectiveMetadata] for all `Directive`-annotated classes visible
|
||||
/// in a file importing `entryPoint`. That is, this includes all
|
||||
/// `Directive` annotated classes in `entryPoint` and any assets which it
|
||||
/// `export`s.
|
||||
/// Returns `null` if there are no `Directive`-annotated classes in
|
||||
/// `entryPoint`.
|
||||
Future<Map<String, DirectiveMetadata>> extractDirectiveMetadata(
|
||||
AssetReader reader, AssetId entryPoint) async {
|
||||
var parser = new Parser(reader);
|
||||
NgDeps ngDeps = await parser.parse(entryPoint);
|
||||
return _extractDirectiveMetadataRecursive(
|
||||
reader, new Parser(reader), entryPoint);
|
||||
}
|
||||
|
||||
var _nullFuture = new Future.value(null);
|
||||
|
||||
Future<Map<String, DirectiveMetadata>> _extractDirectiveMetadataRecursive(
|
||||
AssetReader reader, Parser parser, AssetId entryPoint) async {
|
||||
if (!(await reader.hasInput(entryPoint))) return null;
|
||||
|
||||
var ngDeps = await parser.parse(entryPoint);
|
||||
var baseMap = _metadataMapFromNgDeps(ngDeps);
|
||||
|
||||
return Future.wait(ngDeps.exports.map((export) {
|
||||
var uri = stringLiteralToString(export.uri);
|
||||
if (uri.startsWith('dart:')) return _nullFuture;
|
||||
|
||||
uri = toDepsExtension(uri);
|
||||
var assetId = uriToAssetId(entryPoint, uri, logger, null /* span */);
|
||||
if (assetId == entryPoint) return _nullFuture;
|
||||
return _extractDirectiveMetadataRecursive(reader, parser, assetId)
|
||||
.then((exportMap) {
|
||||
if (exportMap != null) {
|
||||
if (baseMap == null) {
|
||||
baseMap = exportMap;
|
||||
} else {
|
||||
baseMap.addAll(exportMap);
|
||||
}
|
||||
}
|
||||
});
|
||||
})).then((_) => baseMap);
|
||||
}
|
||||
|
||||
Map<String, DirectiveMetadata> _metadataMapFromNgDeps(NgDeps ngDeps) {
|
||||
if (ngDeps == null || ngDeps.registeredTypes.isEmpty) return null;
|
||||
var retVal = <String, DirectiveMetadata>{};
|
||||
ngDeps.registeredTypes.forEach((rType) {
|
||||
|
|
|
@ -36,7 +36,7 @@ class DirectiveMetadataExtractor extends Transformer {
|
|||
jsonMap[k] = directiveMetadataToMap(v);
|
||||
});
|
||||
transform.addOutput(new Asset.fromString(
|
||||
_outputAssetId(fromAssetId), '// ${JSON.encode(jsonMap)}'));
|
||||
_outputAssetId(fromAssetId), JSON.encode(jsonMap)));
|
||||
}
|
||||
} catch (ex, stackTrace) {
|
||||
log.logger.error('Extracting ng metadata failed.\n'
|
||||
|
@ -49,8 +49,5 @@ class DirectiveMetadataExtractor extends Transformer {
|
|||
|
||||
AssetId _outputAssetId(AssetId inputAssetId) {
|
||||
assert(inputAssetId.path.endsWith(DEPS_EXTENSION));
|
||||
var pathIn = inputAssetId.path;
|
||||
return new AssetId(inputAssetId.package,
|
||||
'${pathIn.substring(0, pathIn.length - DEPS_EXTENSION.length)}'
|
||||
'${META_EXTENSION}');
|
||||
return new AssetId(inputAssetId.package, toMetaExtension(inputAssetId.path));
|
||||
}
|
||||
|
|
|
@ -2,9 +2,12 @@ library angular2.test.transform.directive_metadata_extractor.all_tests;
|
|||
|
||||
import 'dart:async';
|
||||
import 'package:angular2/src/render/api.dart';
|
||||
import 'package:angular2/src/render/dom/convert.dart';
|
||||
import 'package:angular2/src/transform/common/directive_metadata_reader.dart';
|
||||
import 'package:angular2/src/transform/common/logging.dart';
|
||||
import 'package:angular2/src/transform/common/parser.dart';
|
||||
import 'package:angular2/src/transform/directive_metadata_extractor/'
|
||||
'extractor.dart';
|
||||
import 'package:barback/barback.dart';
|
||||
import 'package:dart_style/dart_style.dart';
|
||||
import 'package:guinness/guinness.dart';
|
||||
|
@ -24,50 +27,101 @@ void allTests() {
|
|||
return readDirectiveMetadata(ngDeps.registeredTypes.first);
|
||||
}
|
||||
|
||||
it('should parse selectors', () async {
|
||||
var metadata = await readMetadata(
|
||||
'directive_metadata_extractor/directive_metadata_files/selector.ng_deps.dart');
|
||||
expect(metadata.selector).toEqual('hello-app');
|
||||
describe('readMetadata', () {
|
||||
it('should parse selectors', () async {
|
||||
var metadata = await readMetadata(
|
||||
'directive_metadata_extractor/directive_metadata_files/'
|
||||
'selector.ng_deps.dart');
|
||||
expect(metadata.selector).toEqual('hello-app');
|
||||
});
|
||||
|
||||
it('should parse compile children values', () async {
|
||||
var ngDeps = await parser.parse(new AssetId('a',
|
||||
'directive_metadata_extractor/'
|
||||
'directive_metadata_files/compile_children.ng_deps.dart'));
|
||||
var it = ngDeps.registeredTypes.iterator;
|
||||
|
||||
// Unset value defaults to `true`.
|
||||
it.moveNext();
|
||||
expect('${it.current.typeName}').toEqual('UnsetComp');
|
||||
var unsetComp = readDirectiveMetadata(it.current);
|
||||
expect(unsetComp.compileChildren).toBeTrue();
|
||||
|
||||
it.moveNext();
|
||||
expect('${it.current.typeName}').toEqual('FalseComp');
|
||||
var falseComp = readDirectiveMetadata(it.current);
|
||||
expect(falseComp.compileChildren).toBeFalse();
|
||||
|
||||
it.moveNext();
|
||||
expect('${it.current.typeName}').toEqual('TrueComp');
|
||||
var trueComp = readDirectiveMetadata(it.current);
|
||||
expect(trueComp.compileChildren).toBeTrue();
|
||||
});
|
||||
|
||||
it('should parse properties.', () async {
|
||||
var metadata = await readMetadata('directive_metadata_extractor/'
|
||||
'directive_metadata_files/properties.ng_deps.dart');
|
||||
expect(metadata.properties).toBeNotNull();
|
||||
expect(metadata.properties.length).toBe(2);
|
||||
expect(metadata.properties).toContain('key1');
|
||||
expect(metadata.properties['key1']).toEqual('val1');
|
||||
expect(metadata.properties).toContain('key2');
|
||||
expect(metadata.properties['key2']).toEqual('val2');
|
||||
});
|
||||
|
||||
it('should parse host listeners.', () async {
|
||||
var metadata = await readMetadata('directive_metadata_extractor/'
|
||||
'directive_metadata_files/host_listeners.ng_deps.dart');
|
||||
expect(metadata.hostListeners).toBeNotNull();
|
||||
expect(metadata.hostListeners.length).toBe(2);
|
||||
expect(metadata.hostListeners).toContain('change');
|
||||
expect(metadata.hostListeners['change']).toEqual('onChange(\$event)');
|
||||
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',
|
||||
'directive_metadata_extractor/'
|
||||
'directive_metadata_files/too_many_directives.ng_deps.dart'));
|
||||
expect(() => readDirectiveMetadata(ngDeps.registeredTypes.first))
|
||||
.toThrowWith(anInstanceOf: PrintLoggerError);
|
||||
});
|
||||
});
|
||||
|
||||
it('should parse compile children values', () async {
|
||||
var metadata = await readMetadata('directive_metadata_extractor/'
|
||||
'directive_metadata_files/compile_children.ng_deps.dart');
|
||||
expect(metadata.compileChildren).toBeTrue();
|
||||
describe('extractMetadata', () {
|
||||
it('should generate `DirectiveMetadata` from .ng_deps.dart files.',
|
||||
() async {
|
||||
var extracted = await extractDirectiveMetadata(reader, new AssetId(
|
||||
'a', 'directive_metadata_extractor/simple_files/foo.ng_deps.dart'));
|
||||
expect(extracted).toContain('FooComponent');
|
||||
|
||||
metadata = await readMetadata(
|
||||
'directive_metadata_extractor/directive_metadata_files/selector.ng_deps.dart');
|
||||
expect(metadata.compileChildren).toBeFalse();
|
||||
});
|
||||
var extractedMeta = extracted['FooComponent'];
|
||||
expect(extractedMeta.selector).toEqual('[foo]');
|
||||
});
|
||||
|
||||
it('should parse properties.', () async {
|
||||
var metadata = await readMetadata('directive_metadata_extractor/'
|
||||
'directive_metadata_files/properties.ng_deps.dart');
|
||||
expect(metadata.properties).toBeNotNull();
|
||||
expect(metadata.properties.length).toBe(2);
|
||||
expect(metadata.properties).toContain('key1');
|
||||
expect(metadata.properties['key1']).toEqual('val1');
|
||||
expect(metadata.properties).toContain('key2');
|
||||
expect(metadata.properties['key2']).toEqual('val2');
|
||||
});
|
||||
it('should include `DirectiveMetadata` from exported files.', () async {
|
||||
var extracted = await extractDirectiveMetadata(reader, new AssetId(
|
||||
'a', 'directive_metadata_extractor/export_files/foo.ng_deps.dart'));
|
||||
expect(extracted).toContain('FooComponent');
|
||||
expect(extracted).toContain('BarComponent');
|
||||
|
||||
it('should parse host listeners.', () async {
|
||||
var metadata = await readMetadata('directive_metadata_extractor/'
|
||||
'directive_metadata_files/host_listeners.ng_deps.dart');
|
||||
expect(metadata.hostListeners).toBeNotNull();
|
||||
expect(metadata.hostListeners.length).toBe(2);
|
||||
expect(metadata.hostListeners).toContain('change');
|
||||
expect(metadata.hostListeners['change']).toEqual('onChange(\$event)');
|
||||
expect(metadata.hostListeners).toContain('keyDown');
|
||||
expect(metadata.hostListeners['keyDown']).toEqual('onKeyDown(\$event)');
|
||||
});
|
||||
expect(extracted['FooComponent'].selector).toEqual('[foo]');
|
||||
expect(extracted['BarComponent'].selector).toEqual('[bar]');
|
||||
});
|
||||
|
||||
it('should fail when a class is annotated with multiple Directives.',
|
||||
() async {
|
||||
var ngDeps = await parser.parse(new AssetId('a',
|
||||
'directive_metadata_extractor/'
|
||||
'directive_metadata_files/too_many_directives.ng_deps.dart'));
|
||||
expect(() => readDirectiveMetadata(ngDeps.registeredTypes.first))
|
||||
.toThrowWith(anInstanceOf: PrintLoggerError);
|
||||
it('should include `DirectiveMetadata` recursively from exported files.',
|
||||
() async {
|
||||
var extracted = await extractDirectiveMetadata(reader, new AssetId('a',
|
||||
'directive_metadata_extractor/recursive_export_files/foo.ng_deps.dart'));
|
||||
expect(extracted).toContain('FooComponent');
|
||||
expect(extracted).toContain('BarComponent');
|
||||
expect(extracted).toContain('BazComponent');
|
||||
|
||||
expect(extracted['FooComponent'].selector).toEqual('[foo]');
|
||||
expect(extracted['BarComponent'].selector).toEqual('[bar]');
|
||||
expect(extracted['BazComponent'].selector).toEqual('[baz]');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -8,8 +8,18 @@ void initReflector(reflector) {
|
|||
if (_visited) return;
|
||||
_visited = true;
|
||||
reflector
|
||||
..registerType(HelloCmp, {
|
||||
'factory': () => new HelloCmp(),
|
||||
..registerType(UnsetComp, {
|
||||
'factory': () => new UnsetComp(),
|
||||
'parameters': const [const []],
|
||||
'annotations': const [const Directive()]
|
||||
})
|
||||
..registerType(FalseComp, {
|
||||
'factory': () => new FalseComp(),
|
||||
'parameters': const [const []],
|
||||
'annotations': const [const Directive(compileChildren: false)]
|
||||
})
|
||||
..registerType(TrueComp, {
|
||||
'factory': () => new TrueComp(),
|
||||
'parameters': const [const []],
|
||||
'annotations': const [const Directive(compileChildren: true)]
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
library foo.ng_deps.dart;
|
||||
|
||||
import 'foo.dart';
|
||||
import 'bar.dart';
|
||||
import 'package:angular2/src/core/annotations/annotations.dart';
|
||||
|
||||
var _visited = false;
|
||||
|
@ -8,9 +8,9 @@ void initReflector(reflector) {
|
|||
if (_visited) return;
|
||||
_visited = true;
|
||||
reflector
|
||||
..registerType(DependencyComponent, {
|
||||
'factory': () => new DependencyComponent(),
|
||||
..registerType(BarComponent, {
|
||||
'factory': () => new BarComponent(),
|
||||
'parameters': const [],
|
||||
'annotations': const [const Component(selector: '[salad]')]
|
||||
'annotations': const [const Component(selector: '[bar]')]
|
||||
});
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
library foo.ng_deps.dart;
|
||||
|
||||
import 'foo.dart';
|
||||
import 'package:angular2/src/core/annotations/annotations.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, {
|
||||
'factory': () => new FooComponent(),
|
||||
'parameters': const [],
|
||||
'annotations': const [const Component(selector: '[foo]')]
|
||||
});
|
||||
i0.initReflector(reflector);
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
library foo.ng_deps.dart;
|
||||
|
||||
import 'bar.dart';
|
||||
import 'package:angular2/src/core/annotations/annotations.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, {
|
||||
'factory': () => new BarComponent(),
|
||||
'parameters': const [],
|
||||
'annotations': const [const Component(selector: '[bar]')]
|
||||
});
|
||||
i0.initReflector(reflector);
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
library foo.ng_deps.dart;
|
||||
|
||||
import 'baz.dart';
|
||||
import 'package:angular2/src/core/annotations/annotations.dart';
|
||||
|
||||
var _visited = false;
|
||||
void initReflector(reflector) {
|
||||
if (_visited) return;
|
||||
_visited = true;
|
||||
reflector
|
||||
..registerType(BazComponent, {
|
||||
'factory': () => new BazComponent(),
|
||||
'parameters': const [],
|
||||
'annotations': const [const Component(selector: '[baz]')]
|
||||
});
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
library foo.ng_deps.dart;
|
||||
|
||||
import 'foo.dart';
|
||||
import 'package:angular2/src/core/annotations/annotations.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, {
|
||||
'factory': () => new FooComponent(),
|
||||
'parameters': const [],
|
||||
'annotations': const [const Component(selector: '[foo]')]
|
||||
});
|
||||
i0.initReflector(reflector);
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
// {selector: '[salad]', type: 1}
|
|
@ -1,4 +1,4 @@
|
|||
library foo.ng_deps.dart;
|
||||
library bar.ng_deps.dart;
|
||||
|
||||
import 'foo.dart';
|
||||
import 'package:angular2/src/core/annotations/annotations.dart';
|
||||
|
@ -8,9 +8,9 @@ void initReflector(reflector) {
|
|||
if (_visited) return;
|
||||
_visited = true;
|
||||
reflector
|
||||
..registerType(DependencyComponent, {
|
||||
'factory': () => new DependencyComponent(),
|
||||
..registerType(FooComponent, {
|
||||
'factory': () => new FooComponent(),
|
||||
'parameters': const [],
|
||||
'annotations': const [const Component(selector: '[salad]')]
|
||||
'annotations': const [const Component(selector: '[foo]')]
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue