feat(dart/transform): Add DirectiveMetadataExtractor transform step
Add a step that reads `DirectiveMetadata` object off annotated classes into `.ng_meta.dart` files. These will be used by the `TemplateCompiler` step as inputs to the Angular 2 render compiler. Update one test to avoid unsupported functionality, format others.
This commit is contained in:
parent
8e1d53b5e9
commit
0520ca68b4
|
@ -1,9 +1,9 @@
|
|||
library angular2.transform.template_compiler.directive_metadata_reader;
|
||||
library angular2.transform.common.directive_metadata_reader;
|
||||
|
||||
import 'package:analyzer/analyzer.dart';
|
||||
import 'package:angular2/src/render/api.dart';
|
||||
import 'package:angular2/src/transform/common/logging.dart';
|
||||
import 'package:angular2/src/transform/common/parser.dart';
|
||||
import 'logging.dart';
|
||||
import 'parser.dart';
|
||||
|
||||
/// Reads [DirectiveMetadata] from the `attributes` of `t`.
|
||||
DirectiveMetadata readDirectiveMetadata(RegisteredType t) {
|
|
@ -4,6 +4,7 @@ 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 REFLECTION_CAPABILITIES_NAME = 'ReflectionCapabilities';
|
||||
const REGISTER_TYPE_METHOD_NAME = 'registerType';
|
||||
const REGISTER_GETTERS_METHOD_NAME = 'registerGetters';
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
library angular2.transform.directive_metadata_extractor.extractor;
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
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/parser.dart';
|
||||
import 'package:barback/barback.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
|
||||
/// `entryPoint`.
|
||||
Future<Map<String, DirectiveMetadata>> extractDirectiveMetadata(
|
||||
AssetReader reader, AssetId entryPoint) async {
|
||||
var parser = new Parser(reader);
|
||||
NgDeps ngDeps = await parser.parse(entryPoint);
|
||||
if (ngDeps == null || ngDeps.registeredTypes.isEmpty) return null;
|
||||
var retVal = <String, DirectiveMetadata>{};
|
||||
ngDeps.registeredTypes.forEach((rType) {
|
||||
var meta = readDirectiveMetadata(rType);
|
||||
if (meta != null) {
|
||||
retVal['${rType.typeName}'] = meta;
|
||||
}
|
||||
});
|
||||
return retVal;
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
library angular2.transform.directive_metadata_extractor.transformer;
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:angular2/src/render/dom/convert.dart';
|
||||
import 'package:angular2/src/transform/common/asset_reader.dart';
|
||||
import 'package:angular2/src/transform/common/logging.dart' as log;
|
||||
import 'package:angular2/src/transform/common/names.dart';
|
||||
import 'package:barback/barback.dart';
|
||||
|
||||
import 'extractor.dart';
|
||||
|
||||
/// Transformer responsible for processing .ng_deps.dart files created by
|
||||
/// {@link DirectiveProcessor} and creating associated `.ng_meta.dart` files.
|
||||
/// These files contain commented Json-formatted representations of all
|
||||
/// `Directive`s in the associated file.
|
||||
class DirectiveMetadataExtractor extends Transformer {
|
||||
DirectiveMetadataExtractor();
|
||||
|
||||
@override
|
||||
bool isPrimary(AssetId id) => id.path.endsWith(DEPS_EXTENSION);
|
||||
|
||||
@override
|
||||
Future apply(Transform transform) async {
|
||||
log.init(transform);
|
||||
|
||||
try {
|
||||
var reader = new AssetReader.fromTransform(transform);
|
||||
var fromAssetId = transform.primaryInput.id;
|
||||
|
||||
var metadataMap = await extractDirectiveMetadata(reader, fromAssetId);
|
||||
if (metadataMap != null) {
|
||||
var jsonMap = <String, Map>{};
|
||||
metadataMap.forEach((k, v) {
|
||||
jsonMap[k] = directiveMetadataToMap(v);
|
||||
});
|
||||
transform.addOutput(new Asset.fromString(
|
||||
_outputAssetId(fromAssetId), '// ${JSON.encode(jsonMap)}'));
|
||||
}
|
||||
} catch (ex, stackTrace) {
|
||||
log.logger.error('Extracting ng metadata failed.\n'
|
||||
'Exception: $ex\n'
|
||||
'Stack Trace: $stackTrace');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
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}');
|
||||
}
|
|
@ -4,6 +4,7 @@ import 'package:barback/barback.dart';
|
|||
import 'package:dart_style/dart_style.dart';
|
||||
|
||||
import 'directive_linker/transformer.dart';
|
||||
import 'directive_metadata_extractor/transformer.dart';
|
||||
import 'directive_processor/transformer.dart';
|
||||
import 'bind_generator/transformer.dart';
|
||||
import 'reflection_remover/transformer.dart';
|
||||
|
@ -24,7 +25,7 @@ class AngularTransformerGroup extends TransformerGroup {
|
|||
var phases = [
|
||||
[new ReflectionRemover(options)],
|
||||
[new DirectiveProcessor(options)],
|
||||
[new DirectiveLinker()],
|
||||
[new DirectiveLinker(), new DirectiveMetadataExtractor()],
|
||||
[new BindGenerator(options)],
|
||||
[new TemplateCompiler(options)]
|
||||
];
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
library angular2.test.transform.directive_metadata_extractor.all_tests;
|
||||
|
||||
import 'dart:async';
|
||||
import 'package:angular2/src/render/api.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:barback/barback.dart';
|
||||
import 'package:dart_style/dart_style.dart';
|
||||
import 'package:guinness/guinness.dart';
|
||||
|
||||
import '../common/read_file.dart';
|
||||
|
||||
var formatter = new DartFormatter();
|
||||
|
||||
void allTests() {
|
||||
var reader = new TestAssetReader();
|
||||
var parser = new Parser(reader);
|
||||
|
||||
beforeEach(() => setLogger(new PrintLogger()));
|
||||
|
||||
Future<DirectiveMetadata> readMetadata(inputPath) async {
|
||||
var ngDeps = await parser.parse(new AssetId('a', inputPath));
|
||||
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');
|
||||
});
|
||||
|
||||
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();
|
||||
|
||||
metadata = await readMetadata(
|
||||
'directive_metadata_extractor/directive_metadata_files/selector.ng_deps.dart');
|
||||
expect(metadata.compileChildren).toBeFalse();
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
library foo.ng_deps.dart;
|
||||
|
||||
import 'foo.dart';
|
||||
import 'package:angular2/src/core/annotations/annotations.dart';
|
||||
|
||||
var _visited = false;
|
||||
void initReflector(reflector) {
|
||||
if (_visited) return;
|
||||
_visited = true;
|
||||
reflector
|
||||
..registerType(DependencyComponent, {
|
||||
'factory': () => new DependencyComponent(),
|
||||
'parameters': const [],
|
||||
'annotations': const [const Component(selector: '[salad]')]
|
||||
});
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
// {selector: '[salad]', type: 1}
|
|
@ -0,0 +1,16 @@
|
|||
library foo.ng_deps.dart;
|
||||
|
||||
import 'foo.dart';
|
||||
import 'package:angular2/src/core/annotations/annotations.dart';
|
||||
|
||||
var _visited = false;
|
||||
void initReflector(reflector) {
|
||||
if (_visited) return;
|
||||
_visited = true;
|
||||
reflector
|
||||
..registerType(DependencyComponent, {
|
||||
'factory': () => new DependencyComponent(),
|
||||
'parameters': const [],
|
||||
'annotations': const [const Component(selector: '[salad]')]
|
||||
});
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
library angular2.test.transform.integration;
|
||||
|
||||
import 'package:angular2/src/dom/html_adapter.dart';
|
||||
import 'package:angular2/src/transform/common/names.dart';
|
||||
import 'package:angular2/transformer.dart';
|
||||
import 'package:code_transformers/tests.dart';
|
||||
import 'package:dart_style/dart_style.dart';
|
||||
|
@ -62,8 +61,7 @@ void allTests() {
|
|||
'simple_annotation_files/expected/index.ng_deps.dart'
|
||||
}),
|
||||
new IntegrationTestConfig(
|
||||
'should generate proper code for a Component using a selector defined '
|
||||
'in another file.',
|
||||
'should generate proper code for a Component with multiple deps.',
|
||||
inputs: {
|
||||
'a|web/index.dart': 'two_deps_files/index.dart',
|
||||
'a|web/foo.dart': 'two_deps_files/foo.dart',
|
||||
|
|
|
@ -2,7 +2,8 @@ library bar.ng_deps.dart;
|
|||
|
||||
import 'bar.dart';
|
||||
import 'package:angular2/src/core/annotations_impl/annotations.dart';
|
||||
import 'package:angular2/src/core/annotations_impl/annotations.ng_deps.dart' as i0;
|
||||
import 'package:angular2/src/core/annotations_impl/annotations.ng_deps.dart'
|
||||
as i0;
|
||||
import 'foo.dart';
|
||||
import 'foo.ng_deps.dart' as i1;
|
||||
|
||||
|
|
|
@ -2,7 +2,8 @@ library bar.ng_deps.dart;
|
|||
|
||||
import 'bar.dart';
|
||||
import 'package:angular2/src/core/annotations_impl/annotations.dart';
|
||||
import 'package:angular2/src/core/annotations_impl/annotations.ng_deps.dart' as i0;
|
||||
import 'package:angular2/src/core/annotations_impl/annotations.ng_deps.dart'
|
||||
as i0;
|
||||
|
||||
var _visited = false;
|
||||
void initReflector(reflector) {
|
||||
|
|
|
@ -2,7 +2,8 @@ library bar.ng_deps.dart;
|
|||
|
||||
import 'bar.dart';
|
||||
import 'package:angular2/src/core/annotations_impl/annotations.dart';
|
||||
import 'package:angular2/src/core/annotations_impl/annotations.ng_deps.dart' as i0;
|
||||
import 'package:angular2/src/core/annotations_impl/annotations.ng_deps.dart'
|
||||
as i0;
|
||||
|
||||
var _visited = false;
|
||||
void initReflector(reflector) {
|
||||
|
|
|
@ -2,7 +2,8 @@ library bar.ng_deps.dart;
|
|||
|
||||
import 'bar.dart';
|
||||
import 'package:angular2/src/core/annotations_impl/annotations.dart';
|
||||
import 'package:angular2/src/core/annotations_impl/annotations.ng_deps.dart' as i0;
|
||||
import 'package:angular2/src/core/annotations_impl/annotations.ng_deps.dart'
|
||||
as i0;
|
||||
import 'package:angular2/src/core/annotations_impl/view.dart';
|
||||
import 'package:angular2/src/core/annotations_impl/view.ng_deps.dart' as i1;
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ library bar;
|
|||
import 'package:angular2/src/core/annotations_impl/annotations.dart';
|
||||
import 'foo.dart' as prefix;
|
||||
|
||||
@Component(selector: prefix.preDefinedSelector)
|
||||
@Component(selector: 'soup')
|
||||
class MyComponent {
|
||||
final prefix.MyContext c;
|
||||
final String generatedValue;
|
||||
|
|
|
@ -2,7 +2,8 @@ library bar.ng_deps.dart;
|
|||
|
||||
import 'bar.dart';
|
||||
import 'package:angular2/src/core/annotations_impl/annotations.dart';
|
||||
import 'package:angular2/src/core/annotations_impl/annotations.ng_deps.dart' as i0;
|
||||
import 'package:angular2/src/core/annotations_impl/annotations.ng_deps.dart'
|
||||
as i0;
|
||||
import 'foo.dart' as prefix;
|
||||
import 'foo.ng_deps.dart' as i1;
|
||||
|
||||
|
@ -15,8 +16,7 @@ void initReflector(reflector) {
|
|||
'factory':
|
||||
(prefix.MyContext c, String inValue) => new MyComponent(c, inValue),
|
||||
'parameters': const [const [prefix.MyContext], const [String]],
|
||||
'annotations':
|
||||
const [const Component(selector: prefix.preDefinedSelector)]
|
||||
'annotations': const [const Component(selector: 'soup')]
|
||||
});
|
||||
i0.initReflector(reflector);
|
||||
i1.initReflector(reflector);
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
library foo;
|
||||
|
||||
const preDefinedSelector = 'soup';
|
||||
|
||||
class MyContext {
|
||||
final String selector;
|
||||
const MyContext(this.selector);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
library angular2.test.transform.debug_reflection_remover_files;
|
||||
library angular2.test.transform.reflection_remover.debug_mirrors_files.expected;
|
||||
|
||||
// This file is intentionally formatted as a string to avoid having the
|
||||
// automatic transformer prettify it.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
library angular2.test.transform.reflection_remover_files;
|
||||
library angular2.test.transform.reflection_remover.reflection_remover_files;
|
||||
|
||||
// This file is intentionally formatted as a string to avoid having the
|
||||
// automatic transformer prettify it.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
library angular2.test.transform.debug_reflection_remover_files;
|
||||
library angular2.test.transform.reflection_remover.verbose_files.expected;
|
||||
|
||||
// This file is intentionally formatted as a string to avoid having the
|
||||
// automatic transformer prettify it.
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
library angular2.test.transform.template_compiler.all_tests;
|
||||
|
||||
import 'dart:async';
|
||||
import 'package:barback/barback.dart';
|
||||
import 'package:angular2/src/dom/html_adapter.dart';
|
||||
import 'package:angular2/src/render/api.dart';
|
||||
import 'package:angular2/src/transform/common/asset_reader.dart';
|
||||
import 'package:angular2/src/transform/common/logging.dart';
|
||||
import 'package:angular2/src/transform/common/parser.dart';
|
||||
import 'package:angular2/src/transform/template_compiler/directive_metadata_reader.dart';
|
||||
import 'package:angular2/src/transform/template_compiler/generator.dart';
|
||||
import 'package:dart_style/dart_style.dart';
|
||||
import 'package:guinness/guinness.dart';
|
||||
|
@ -19,7 +15,6 @@ var formatter = new DartFormatter();
|
|||
void allTests() {
|
||||
Html5LibDomAdapter.makeCurrent();
|
||||
AssetReader reader = new TestAssetReader();
|
||||
var parser = new Parser(reader);
|
||||
|
||||
beforeEach(() => setLogger(new PrintLogger()));
|
||||
|
||||
|
@ -63,59 +58,6 @@ void allTests() {
|
|||
var output = await processTemplates(reader, new AssetId('a', inputPath));
|
||||
_formatThenExpectEquals(output, expected);
|
||||
});
|
||||
|
||||
describe('DirectiveMetadataReader', () {
|
||||
Future<DirectiveMetadata> readMetadata(inputPath) async {
|
||||
var ngDeps = await parser.parse(new AssetId('a', inputPath));
|
||||
return readDirectiveMetadata(ngDeps.registeredTypes.first);
|
||||
}
|
||||
|
||||
it('should parse selectors', () async {
|
||||
var metadata = await readMetadata(
|
||||
'template_compiler/directive_metadata_files/selector.ng_deps.dart');
|
||||
expect(metadata.selector).toEqual('hello-app');
|
||||
});
|
||||
|
||||
it('should parse compile children values', () async {
|
||||
var metadata = await readMetadata('template_compiler/'
|
||||
'directive_metadata_files/compile_children.ng_deps.dart');
|
||||
expect(metadata.compileChildren).toBeTrue();
|
||||
|
||||
metadata = await readMetadata(
|
||||
'template_compiler/directive_metadata_files/selector.ng_deps.dart');
|
||||
expect(metadata.compileChildren).toBeFalse();
|
||||
});
|
||||
|
||||
it('should parse properties.', () async {
|
||||
var metadata = await readMetadata('template_compiler/'
|
||||
'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('template_compiler/'
|
||||
'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', 'template_compiler/'
|
||||
'directive_metadata_files/too_many_directives.ng_deps.dart'));
|
||||
expect(() => readDirectiveMetadata(ngDeps.registeredTypes.first))
|
||||
.toThrowWith(anInstanceOf: PrintLoggerError);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void _formatThenExpectEquals(String actual, String expected) {
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:unittest/vm_config.dart';
|
|||
|
||||
import 'bind_generator/all_tests.dart' as bindGenerator;
|
||||
import 'directive_linker/all_tests.dart' as directiveLinker;
|
||||
import 'directive_metadata_extractor/all_tests.dart' as directiveMeta;
|
||||
import 'directive_processor/all_tests.dart' as directiveProcessor;
|
||||
import 'integration/all_tests.dart' as integration;
|
||||
import 'reflection_remover/all_tests.dart' as reflectionRemover;
|
||||
|
@ -15,6 +16,7 @@ main() {
|
|||
useVMConfiguration();
|
||||
describe('Bind Generator', bindGenerator.allTests);
|
||||
describe('Directive Linker', directiveLinker.allTests);
|
||||
describe('Directive Metadata Extractor', directiveMeta.allTests);
|
||||
describe('Directive Processor', directiveProcessor.allTests);
|
||||
describe('Reflection Remover', reflectionRemover.allTests);
|
||||
describe('Template Compiler', templateCompiler.allTests);
|
||||
|
|
Loading…
Reference in New Issue