Skip to content

Commit 08b56e1

Browse files
author
Tim Blasi
committed
feat(dart/transform): Add simple ParseTemplates step
Generate methods in the ParseTemplates step. Add a test for inline template method generation.
1 parent b3fa1fa commit 08b56e1

File tree

14 files changed

+194
-143
lines changed

14 files changed

+194
-143
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,6 @@ const SETUP_METHOD_NAME = 'setupReflection';
44
const REFLECTOR_VAR_NAME = 'reflector';
55
const DEPS_EXTENSION = '.ng_deps.dart';
66
const REGISTER_TYPE_METHOD_NAME = 'registerType';
7+
const REGISTER_GETTERS_METHOD_NAME = 'registerGetters';
78
const REGISTER_SETTERS_METHOD_NAME = 'registerSetters';
9+
const REGISTER_METHODS_METHOD_NAME = 'registerMethods';

modules/angular2/src/transform/template_parser/generator.dart renamed to modules/angular2/src/transform/template_compiler/generator.dart

Lines changed: 43 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,30 @@
1-
library angular2.src.transform.template_parser.generator;
1+
library angular2.src.transform.template_compiler.generator;
22

33
import 'dart:async';
44

55
import 'package:analyzer/analyzer.dart';
6-
import 'package:angular2/src/change_detection/parser/ast.dart';
76
import 'package:angular2/src/change_detection/parser/lexer.dart' as ng;
87
import 'package:angular2/src/change_detection/parser/parser.dart' as ng;
9-
import 'package:angular2/src/core/compiler/pipeline/compile_element.dart';
108
import 'package:angular2/src/core/compiler/pipeline/compile_pipeline.dart';
119
import 'package:angular2/src/core/compiler/pipeline/compile_step.dart';
1210
import 'package:angular2/src/core/compiler/pipeline/property_binding_parser.dart';
1311
import 'package:angular2/src/core/compiler/pipeline/text_interpolation_parser.dart';
1412
import 'package:angular2/src/core/compiler/pipeline/view_splitter.dart';
1513
import 'package:angular2/src/dom/dom_adapter.dart';
16-
import 'package:angular2/src/dom/html5lib_adapter.dart';
1714
import 'package:angular2/src/reflection/reflection.dart';
1815
import 'package:angular2/src/transform/common/asset_reader.dart';
1916
import 'package:angular2/src/transform/common/logging.dart';
2017
import 'package:angular2/src/transform/common/names.dart';
2118
import 'package:angular2/src/transform/common/parser.dart';
2219
import 'package:barback/barback.dart';
23-
import 'package:code_transformers/assets.dart';
2420

2521
import 'recording_reflection_capabilities.dart';
2622

23+
/// Reads the `.ng_deps.dart` file represented by `entryPoint` and parses any
24+
/// Angular 2 `Template` annotations it declares to generate `getter`s,
25+
/// `setter`s, and `method`s that would otherwise be reflectively accessed.
26+
///
27+
/// This method assumes a [DomAdapter] has been registered.
2728
Future<String> processTemplates(AssetReader reader, AssetId entryPoint) async {
2829
var parser = new Parser(reader);
2930
NgDeps ngDeps = await parser.parse(entryPoint);
@@ -34,66 +35,71 @@ Future<String> processTemplates(AssetReader reader, AssetId entryPoint) async {
3435
var values = _processTemplate(templateText);
3536
var calls = _generateGetters('${rType.typeName}', values.getterNames);
3637
if (calls.isNotEmpty) {
37-
registrations.write('..registerGetters({${calls.join(', ')}})');
38+
registrations.write('..${REGISTER_GETTERS_METHOD_NAME}'
39+
'({${calls.join(', ')}})');
3840
}
3941
calls = _generateSetters('${rType.typeName}', values.setterNames);
4042
if (calls.isNotEmpty) {
41-
registrations.write('..registerSetters({${calls.join(', ')}})');
43+
registrations.write('..${REGISTER_SETTERS_METHOD_NAME}'
44+
'({${calls.join(', ')}})');
45+
}
46+
calls = _generateMethods('${rType.typeName}', values.methodNames);
47+
if (calls.isNotEmpty) {
48+
registrations.write('..${REGISTER_METHODS_METHOD_NAME}'
49+
'({${calls.join(', ')}})');
4250
}
4351
});
4452
});
4553

46-
String code = ngDeps.code;
54+
var code = ngDeps.code;
4755
if (registrations.length == 0) return code;
4856
var codeInjectIdx = ngDeps.registeredTypes.last.registerMethod.end;
4957
return '${code.substring(0, codeInjectIdx)}'
5058
'${registrations}'
5159
'${code.substring(codeInjectIdx)}';
5260
}
5361

62+
Iterable<String> _generateGetters(String typeName, List<String> getterNames) {
63+
return getterNames.map((prop) {
64+
// TODO(kegluneq): Include `typeName` where possible.
65+
return ''''$prop': (o) => o.$prop''';
66+
});
67+
}
68+
69+
Iterable<String> _generateSetters(String typeName, List<String> setterName) {
70+
return setterName.map((prop) {
71+
return ''''$prop': (o, v) => o.$prop = v''';
72+
});
73+
}
74+
75+
Iterable<String> _generateMethods(String typeName, List<String> methodNames) {
76+
return methodNames.map((methodName) {
77+
return ''''$methodName': (o, List args) =>
78+
Function.apply(o.$methodName, args)''';
79+
});
80+
}
81+
5482
RecordingReflectionCapabilities _processTemplate(String templateCode) {
5583
var recordingCapabilities = new RecordingReflectionCapabilities();
84+
var savedReflectionCapabilities = reflector.reflectionCapabilities;
5685
reflector.reflectionCapabilities = recordingCapabilities;
5786

58-
var compilePipeline = new CompilePipeline(createCompileSteps());
87+
var compilePipeline = new CompilePipeline(_createCompileSteps());
5988
var template = DOM.createTemplate(templateCode);
6089
// TODO(kegluneq): Need to parse this from a file when not inline.
6190
compilePipeline.process(template, templateCode);
6291

92+
reflector.reflectionCapabilities = savedReflectionCapabilities;
6393
return recordingCapabilities;
6494
}
6595

66-
List<String> _generateGetters(String typeName, List<String> getterNames) {
67-
var getters = [];
68-
getterNames.forEach((prop) {
69-
// TODO(kegluneq): Include `typeName` where possible.
70-
getters.add('\'$prop\': (o) => o.$prop');
71-
});
72-
return getters;
73-
}
74-
75-
List<String> _generateSetters(String typeName, List<String> setterName) {
76-
var setters = [];
77-
setterName.forEach((prop) {
78-
// TODO(kegluneq): Include `typeName` where possible.
79-
setters.add('\'$prop\': (o, String v) => o.$prop = v');
80-
});
81-
return setters;
82-
}
83-
84-
List<CompileStep> createCompileSteps() {
96+
List<CompileStep> _createCompileSteps() {
8597
var parser = new ng.Parser(new ng.Lexer());
98+
// TODO(kegluneq): Add other compile steps from default_steps.dart.
8699
return [
87100
new ViewSplitter(parser),
88-
// cssProcessor.getCompileStep(
89-
// compiledComponent, shadowDomStrategy, templateUrl),
90101
new PropertyBindingParser(parser),
91-
// new DirectiveParser(directives),
92102
new TextInterpolationParser(parser)
93-
// new ElementBindingMarker(),
94-
// new ProtoViewBuilder(changeDetection, shadowDomStrategy),
95-
// new ProtoElementInjectorBuilder(),
96-
// new ElementBinderBuilder(parser)
97103
];
98104
}
99105

@@ -103,6 +109,8 @@ List<String> _processRegisteredType(AssetReader reader, RegisteredType t) {
103109
return visitor.templateText;
104110
}
105111

112+
/// Visitor responsible for processing the `annotations` property of a
113+
/// [RegisterType] object and pulling out template text.
106114
class _TemplateExtractVisitor extends Object with RecursiveAstVisitor<Object> {
107115
final List<String> templateText = [];
108116
final AssetReader _reader;

modules/angular2/src/transform/template_parser/recording_reflection_capabilities.dart renamed to modules/angular2/src/transform/template_compiler/recording_reflection_capabilities.dart

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
1-
library angular2.src.transform.template_parser.recording_reflection_capabilities;
1+
library angular2.src.transform.template_compiler.recording_reflection_capabilities;
22

33
import 'package:angular2/src/reflection/reflection_capabilities.dart';
44
import 'package:angular2/src/reflection/types.dart';
55

6+
/// ReflectionCapabilities object that records requests for `getter`s,
7+
/// `setter`s, and `method`s so these can be code generated rather than
8+
/// reflectively accessed at runtime.
69
class RecordingReflectionCapabilities implements ReflectionCapabilities {
7-
void _notImplemented(String name) {
8-
throw 'Not implemented: $name';
9-
}
10-
10+
/// The names of all requested `getter`s.
1111
final List<String> getterNames = [];
12+
/// The names of all requested `setter`s.
1213
final List<String> setterNames = [];
14+
/// The names of all requested `method`s.
1315
final List<String> methodNames = [];
1416

17+
void _notImplemented(String name) {
18+
throw 'Not implemented: $name';
19+
}
20+
1521
Function factory(Type type) => _notImplemented('factory');
1622

1723
List<List> parameters(typeOrFunc) => _notImplemented('parameters');
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
library angular2.src.transform.template_compiler.transformer;
2+
3+
import 'dart:async';
4+
5+
import 'package:angular2/src/dom/html5lib_adapter.dart';
6+
import 'package:angular2/src/transform/common/asset_reader.dart';
7+
import 'package:angular2/src/transform/common/formatter.dart';
8+
import 'package:angular2/src/transform/common/logging.dart' as log;
9+
import 'package:angular2/src/transform/common/names.dart';
10+
import 'package:angular2/src/transform/common/options.dart';
11+
import 'package:barback/barback.dart';
12+
13+
import 'generator.dart';
14+
15+
/// [Transformer] responsible for detecting and processing Angular 2 templates.
16+
///
17+
/// [TemplateComplier] uses the Angular 2 `Compiler` to process the templates,
18+
/// extracting information about what reflection is necessary to render and
19+
/// use that template. It then generates code in place of those reflective
20+
/// accesses.
21+
class TemplateComplier extends Transformer {
22+
final TransformerOptions options;
23+
24+
TemplateComplier(this.options);
25+
26+
@override
27+
bool isPrimary(AssetId id) => id.path.endsWith(DEPS_EXTENSION);
28+
29+
@override
30+
Future apply(Transform transform) async {
31+
log.init(transform);
32+
33+
try {
34+
Html5LibDomAdapter.makeCurrent();
35+
var id = transform.primaryInput.id;
36+
var reader = new AssetReader.fromTransform(transform);
37+
var transformedCode =
38+
formatter.format(await processTemplates(reader, id));
39+
transform.addOutput(new Asset.fromString(id, transformedCode));
40+
} catch (ex, stackTrace) {
41+
log.logger.error('Parsing ng templates failed.\n'
42+
'Exception: $ex\n'
43+
'Stack Trace: $stackTrace');
44+
}
45+
}
46+
}

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

Lines changed: 0 additions & 65 deletions
This file was deleted.

modules/angular2/src/transform/transformer.dart

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,20 @@ import 'directive_linker/transformer.dart';
77
import 'directive_processor/transformer.dart';
88
import 'bind_generator/transformer.dart';
99
import 'reflection_remover/transformer.dart';
10-
import 'template_parser/transformer.dart';
10+
import 'template_compiler/transformer.dart';
1111
import 'common/formatter.dart' as formatter;
1212
import 'common/options.dart';
1313

1414
export 'common/options.dart';
1515

16-
/// Removes the mirror-based initialization logic and replaces it with static
17-
/// logic.
16+
/// Replaces Angular 2 mirror use with generated code.
1817
class AngularTransformerGroup extends TransformerGroup {
1918
AngularTransformerGroup(TransformerOptions options) : super([
2019
[new DirectiveProcessor(options)],
2120
[new DirectiveLinker(options)],
22-
[new BindGenerator(options), new ReflectionRemover(options)],
23-
[new TemplateParser(options)]
21+
[new BindGenerator(options)],
22+
[new TemplateComplier(options)],
23+
[new ReflectionRemover(options)]
2424
]) {
2525
formatter.init(new DartFormatter());
2626
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
library angular2.test.transform.integration;
22

33
import 'dart:io';
4+
import 'package:angular2/src/dom/html5lib_adapter.dart';
45
import 'package:angular2/transformer.dart';
56
import 'package:code_transformers/tests.dart';
67
import 'package:dart_style/dart_style.dart';
@@ -24,6 +25,8 @@ class IntegrationTestConfig {
2425
}
2526

2627
void allTests() {
28+
Html5LibDomAdapter.makeCurrent();
29+
2730
/*
2831
* Each test has its own directory for inputs & an `expected` directory for
2932
* expected outputs.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
library angular2.test.transform.directive_processor.all_tests;
2+
3+
import 'package:barback/barback.dart';
4+
import 'package:angular2/src/dom/html5lib_adapter.dart';
5+
import 'package:angular2/src/transform/common/asset_reader.dart';
6+
import 'package:angular2/src/transform/common/formatter.dart';
7+
import 'package:angular2/src/transform/template_compiler/generator.dart';
8+
import 'package:dart_style/dart_style.dart';
9+
import 'package:unittest/unittest.dart';
10+
11+
import '../common/read_file.dart';
12+
13+
var formatter = new DartFormatter();
14+
15+
void allTests() {
16+
Html5LibDomAdapter.makeCurrent();
17+
AssetReader reader = new TestAssetReader();
18+
19+
test('should parse simple expressions in inline templates.', () async {
20+
var inputPath =
21+
'template_compiler/inline_expression_files/hello.ng_deps.dart';
22+
var expected = readFile(
23+
'template_compiler/inline_expression_files/expected/hello.ng_deps.dart');
24+
var output = await processTemplates(reader, new AssetId('a', inputPath));
25+
output = formatter.format(output);
26+
expected = formatter.format(expected);
27+
expect(output, equals(expected));
28+
});
29+
30+
test('should parse simple methods in inline templates.', () async {
31+
var inputPath = 'template_compiler/inline_method_files/hello.ng_deps.dart';
32+
var expected = readFile(
33+
'template_compiler/inline_method_files/expected/hello.ng_deps.dart');
34+
var output = await processTemplates(reader, new AssetId('a', inputPath));
35+
output = formatter.format(output);
36+
expected = formatter.format(expected);
37+
expect(output, equals(expected));
38+
});
39+
}

modules/angular2/test/transform/template_parser/basic_files/expected/hello.ngDeps.dart renamed to modules/angular2/test/transform/template_compiler/inline_expression_files/expected/hello.ng_deps.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@ void setupReflection(reflector) {
1818
]
1919
})
2020
..registerGetters({'greeting': (o) => o.greeting})
21-
..registerSetters({'greeting': (o, String v) => o.greeting = v});
21+
..registerSetters({'greeting': (o, v) => o.greeting = v});
2222
}

0 commit comments

Comments
 (0)