refactor(dart/transform): Use render Compiler concepts

Update `TemplateCompiler` transform step to use abstractions used by the
render `Compiler`. For example, template code is now loaded via an
instance of `TemplateLoader` and external resources are fetched via an
instance of `XHR`.
This commit is contained in:
Tim Blasi 2015-05-01 17:29:58 -07:00
parent abc3de7efe
commit 169e4e862d
5 changed files with 111 additions and 76 deletions

View File

@ -296,7 +296,9 @@ class BrowserDomAdapter extends GenericBrowserDomAdapter {
} }
String getEventKey(KeyboardEvent event) { String getEventKey(KeyboardEvent event) {
int keyCode = event.keyCode; int keyCode = event.keyCode;
return _keyCodeToKeyMap.containsKey(keyCode) ? _keyCodeToKeyMap[keyCode] : 'Unidentified'; return _keyCodeToKeyMap.containsKey(keyCode)
? _keyCodeToKeyMap[keyCode]
: 'Unidentified';
} }
getGlobalEventTarget(String target) { getGlobalEventTarget(String target) {
if (target == "window") { if (target == "window") {

View File

@ -168,9 +168,8 @@ class Html5LibDomAdapter implements DomAdapter {
getHost(el) { getHost(el) {
throw 'not implemented'; throw 'not implemented';
} }
clone(node) { clone(node) => node.clone(true);
throw 'not implemented';
}
hasProperty(element, String name) { hasProperty(element, String name) {
throw 'not implemented'; throw 'not implemented';
} }

View File

@ -0,0 +1,26 @@
library angular2.transform.template_compiler.compile_step_factory;
import 'dart:async';
import 'package:angular2/src/change_detection/parser/parser.dart' as ng;
import 'package:angular2/src/render/api.dart';
import 'package:angular2/src/render/dom/compiler/compile_step.dart';
import 'package:angular2/src/render/dom/compiler/compile_step_factory.dart'
as base;
import 'package:angular2/src/render/dom/compiler/property_binding_parser.dart';
import 'package:angular2/src/render/dom/compiler/text_interpolation_parser.dart';
import 'package:angular2/src/render/dom/compiler/view_splitter.dart';
class CompileStepFactory implements base.CompileStepFactory {
final ng.Parser _parser;
CompileStepFactory(this._parser);
List<CompileStep> createSteps(
ViewDefinition template, List<Future> subTaskPromises) {
// TODO(kegluneq): Add other compile steps from default_steps.dart.
return [
new ViewSplitter(_parser),
new PropertyBindingParser(_parser),
new TextInterpolationParser(_parser)
];
}
}

View File

@ -5,22 +5,22 @@ import 'dart:async';
import 'package:analyzer/analyzer.dart'; import 'package:analyzer/analyzer.dart';
import 'package:angular2/src/change_detection/parser/lexer.dart' as ng; import 'package:angular2/src/change_detection/parser/lexer.dart' as ng;
import 'package:angular2/src/change_detection/parser/parser.dart' as ng; import 'package:angular2/src/change_detection/parser/parser.dart' as ng;
import 'package:angular2/src/render/api.dart';
import 'package:angular2/src/render/dom/compiler/compile_pipeline.dart'; import 'package:angular2/src/render/dom/compiler/compile_pipeline.dart';
import 'package:angular2/src/render/dom/compiler/compile_step.dart'; import 'package:angular2/src/render/dom/compiler/template_loader.dart';
import 'package:angular2/src/render/dom/compiler/property_binding_parser.dart'; import "package:angular2/src/services/xhr.dart" show XHR;
import 'package:angular2/src/render/dom/compiler/text_interpolation_parser.dart';
import 'package:angular2/src/render/dom/compiler/view_splitter.dart';
import 'package:angular2/src/dom/dom_adapter.dart';
import 'package:angular2/src/reflection/reflection.dart'; import 'package:angular2/src/reflection/reflection.dart';
import 'package:angular2/src/services/url_resolver.dart';
import 'package:angular2/src/transform/common/asset_reader.dart'; import 'package:angular2/src/transform/common/asset_reader.dart';
import 'package:angular2/src/transform/common/logging.dart'; import 'package:angular2/src/transform/common/logging.dart';
import 'package:angular2/src/transform/common/names.dart'; import 'package:angular2/src/transform/common/names.dart';
import 'package:angular2/src/transform/common/parser.dart'; import 'package:angular2/src/transform/common/parser.dart';
import 'package:angular2/src/transform/common/property_utils.dart' as prop; import 'package:angular2/src/transform/common/property_utils.dart' as prop;
import 'package:barback/barback.dart'; import 'package:barback/barback.dart';
import 'package:code_transformers/assets.dart';
import 'compile_step_factory.dart';
import 'recording_reflection_capabilities.dart'; import 'recording_reflection_capabilities.dart';
import 'xhr_impl.dart';
/// Reads the `.ng_deps.dart` file represented by `entryPoint` and parses any /// Reads the `.ng_deps.dart` file represented by `entryPoint` and parses any
/// Angular 2 `View` annotations it declares to generate `getter`s, /// Angular 2 `View` annotations it declares to generate `getter`s,
@ -30,28 +30,27 @@ import 'recording_reflection_capabilities.dart';
Future<String> processTemplates(AssetReader reader, AssetId entryPoint) async { Future<String> processTemplates(AssetReader reader, AssetId entryPoint) async {
var parser = new Parser(reader); var parser = new Parser(reader);
NgDeps ngDeps = await parser.parse(entryPoint); NgDeps ngDeps = await parser.parse(entryPoint);
var extractor = new _TemplateExtractor(reader, entryPoint); var extractor = new _TemplateExtractor(new XhrImpl(reader, entryPoint));
var registrations = new StringBuffer(); var registrations = new StringBuffer();
for (var rType in ngDeps.registeredTypes) { for (var rType in ngDeps.registeredTypes) {
(await extractor.extractTemplates(rType)) var values = await extractor.extractTemplates(rType);
.forEach((RecordingReflectionCapabilities values) { if (values == null) continue;
var calls = _generateGetters('${rType.typeName}', values.getterNames); var calls = _generateGetters('${rType.typeName}', values.getterNames);
if (calls.isNotEmpty) { if (calls.isNotEmpty) {
registrations.write('..${REGISTER_GETTERS_METHOD_NAME}' registrations.write('..${REGISTER_GETTERS_METHOD_NAME}'
'({${calls.join(', ')}})'); '({${calls.join(', ')}})');
} }
calls = _generateSetters('${rType.typeName}', values.setterNames); calls = _generateSetters('${rType.typeName}', values.setterNames);
if (calls.isNotEmpty) { if (calls.isNotEmpty) {
registrations.write('..${REGISTER_SETTERS_METHOD_NAME}' registrations.write('..${REGISTER_SETTERS_METHOD_NAME}'
'({${calls.join(', ')}})'); '({${calls.join(', ')}})');
} }
calls = _generateMethods('${rType.typeName}', values.methodNames); calls = _generateMethods('${rType.typeName}', values.methodNames);
if (calls.isNotEmpty) { if (calls.isNotEmpty) {
registrations.write('..${REGISTER_METHODS_METHOD_NAME}' registrations.write('..${REGISTER_METHODS_METHOD_NAME}'
'({${calls.join(', ')}})'); '({${calls.join(', ')}})');
} }
});
} }
var code = ngDeps.code; var code = ngDeps.code;
@ -105,75 +104,52 @@ Iterable<String> _generateMethods(
/// template code if necessary, and determines what values will be /// template code if necessary, and determines what values will be
/// reflectively accessed from that template. /// reflectively accessed from that template.
class _TemplateExtractor { class _TemplateExtractor {
final AssetReader _reader; final CompileStepFactory _factory;
final AssetId _entryPoint;
final CompilePipeline _pipeline;
final _TemplateExtractVisitor _visitor = new _TemplateExtractVisitor(); final _TemplateExtractVisitor _visitor = new _TemplateExtractVisitor();
final TemplateLoader _loader;
_TemplateExtractor(this._reader, this._entryPoint) _TemplateExtractor(XHR xhr)
: _pipeline = new CompilePipeline(_createCompileSteps()); : _loader = new TemplateLoader(xhr, new UrlResolver()),
_factory = new CompileStepFactory(new ng.Parser(new ng.Lexer()));
static List<CompileStep> _createCompileSteps() { Future<RecordingReflectionCapabilities> extractTemplates(RegisteredType t) {
var parser = new ng.Parser(new ng.Lexer()); return _processTemplate(_processRegisteredType(t));
// TODO(kegluneq): Add other compile steps from default_steps.dart.
return [
new ViewSplitter(parser),
new PropertyBindingParser(parser),
new TextInterpolationParser(parser)
];
} }
Future<List<RecordingReflectionCapabilities>> extractTemplates( Future<RecordingReflectionCapabilities> _processTemplate(
RegisteredType t) async { ViewDefinition viewDef) async {
return (await _processRegisteredType(t)).map(_processTemplate).toList(); // Check for "imperative views".
} if (viewDef.template == null && viewDef.absUrl == null) return null;
RecordingReflectionCapabilities _processTemplate(String templateCode) {
var recordingCapabilities = new RecordingReflectionCapabilities(); var recordingCapabilities = new RecordingReflectionCapabilities();
var savedReflectionCapabilities = reflector.reflectionCapabilities; var savedReflectionCapabilities = reflector.reflectionCapabilities;
reflector.reflectionCapabilities = recordingCapabilities; reflector.reflectionCapabilities = recordingCapabilities;
_pipeline.process(DOM.createTemplate(templateCode), templateCode); // TODO(kegluneq): Rewrite url to inline `template` where possible.
// See [https://github.com/angular/angular/issues/1035].
var domNode = await _loader.load(viewDef);
new CompilePipeline(_factory.createSteps(viewDef, [])).process(
domNode, '$domNode');
reflector.reflectionCapabilities = savedReflectionCapabilities; reflector.reflectionCapabilities = savedReflectionCapabilities;
return recordingCapabilities; return recordingCapabilities;
} }
Future<List<String>> _processRegisteredType(RegisteredType t) async { ViewDefinition _processRegisteredType(RegisteredType t) {
_visitor.reset(); _visitor.reset();
t.annotations.accept(_visitor); t.annotations.accept(_visitor);
var toReturn = _visitor.inlineValues; return _visitor.viewDef;
for (var url in _visitor.urlValues) {
var templateText = await _readUrlTemplate(url);
if (templateText != null) {
toReturn.add(templateText);
}
}
return toReturn;
}
// TODO(kegluneq): Rewrite these to `template` where possible.
// See [https://github.com/angular/angular/issues/1035].
Future<String> _readUrlTemplate(String url) async {
var assetId = uriToAssetId(_entryPoint, url, logger, null);
var templateExists = await _reader.hasInput(assetId);
if (!templateExists) {
logger.error('Could not read template at uri $url from $_entryPoint');
return null;
}
return await _reader.readAsString(assetId);
} }
} }
/// Visitor responsible for processing the `annotations` property of a /// Visitor responsible for processing the `annotations` property of a
/// {@link RegisterType} object and pulling out template text. /// {@link RegisterType} object and pulling out template information.
class _TemplateExtractVisitor extends Object with RecursiveAstVisitor<Object> { class _TemplateExtractVisitor extends Object with RecursiveAstVisitor<Object> {
final List<String> inlineValues = []; ViewDefinition viewDef = new ViewDefinition();
final List<String> urlValues = [];
void reset() { void reset() {
inlineValues.clear(); viewDef = new ViewDefinition();
urlValues.clear();
} }
@override @override
@ -195,9 +171,16 @@ class _TemplateExtractVisitor extends Object with RecursiveAstVisitor<Object> {
} }
var valueString = stringLiteralToString(node.expression); var valueString = stringLiteralToString(node.expression);
if (keyString == 'templateUrl') { if (keyString == 'templateUrl') {
urlValues.add(valueString); if (viewDef.absUrl != null) {
logger.error(
'Found multiple values for "templateUrl". Source: ${node}');
}
viewDef.absUrl = valueString;
} else { } else {
inlineValues.add(valueString); if (viewDef.template != null) {
logger.error('Found multiple values for "template". Source: ${node}');
}
viewDef.template = valueString;
} }
} }
return null; return null;

View File

@ -0,0 +1,25 @@
library angular2.transform.template_compiler.xhr_impl;
import 'dart:async';
import 'package:angular2/src/services/xhr.dart' show XHR;
import 'package:angular2/src/transform/common/asset_reader.dart';
import 'package:angular2/src/transform/common/logging.dart';
import 'package:barback/barback.dart';
import 'package:code_transformers/assets.dart';
class XhrImpl implements XHR {
final AssetReader _reader;
final AssetId _entryPoint;
XhrImpl(this._reader, this._entryPoint);
Future<String> get(String url) async {
var assetId = uriToAssetId(_entryPoint, url, logger, null);
var templateExists = await _reader.hasInput(assetId);
if (!templateExists) {
logger.error('Could not read template at uri $url from $_entryPoint');
return null;
}
return await _reader.readAsString(assetId);
}
}