From 169e4e862d6f829cb8f8af96a8fd6d709a0595d0 Mon Sep 17 00:00:00 2001 From: Tim Blasi Date: Fri, 1 May 2015 17:29:58 -0700 Subject: [PATCH] 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`. --- modules/angular2/src/dom/browser_adapter.dart | 4 +- modules/angular2/src/dom/html_adapter.dart | 5 +- .../compile_step_factory.dart | 26 ++++ .../template_compiler/generator.dart | 127 ++++++++---------- .../transform/template_compiler/xhr_impl.dart | 25 ++++ 5 files changed, 111 insertions(+), 76 deletions(-) create mode 100644 modules/angular2/src/transform/template_compiler/compile_step_factory.dart create mode 100644 modules/angular2/src/transform/template_compiler/xhr_impl.dart diff --git a/modules/angular2/src/dom/browser_adapter.dart b/modules/angular2/src/dom/browser_adapter.dart index f33c03d825..b9763886a6 100644 --- a/modules/angular2/src/dom/browser_adapter.dart +++ b/modules/angular2/src/dom/browser_adapter.dart @@ -296,7 +296,9 @@ class BrowserDomAdapter extends GenericBrowserDomAdapter { } String getEventKey(KeyboardEvent event) { int keyCode = event.keyCode; - return _keyCodeToKeyMap.containsKey(keyCode) ? _keyCodeToKeyMap[keyCode] : 'Unidentified'; + return _keyCodeToKeyMap.containsKey(keyCode) + ? _keyCodeToKeyMap[keyCode] + : 'Unidentified'; } getGlobalEventTarget(String target) { if (target == "window") { diff --git a/modules/angular2/src/dom/html_adapter.dart b/modules/angular2/src/dom/html_adapter.dart index 63a3af3772..c74537a81d 100644 --- a/modules/angular2/src/dom/html_adapter.dart +++ b/modules/angular2/src/dom/html_adapter.dart @@ -168,9 +168,8 @@ class Html5LibDomAdapter implements DomAdapter { getHost(el) { throw 'not implemented'; } - clone(node) { - throw 'not implemented'; - } + clone(node) => node.clone(true); + hasProperty(element, String name) { throw 'not implemented'; } diff --git a/modules/angular2/src/transform/template_compiler/compile_step_factory.dart b/modules/angular2/src/transform/template_compiler/compile_step_factory.dart new file mode 100644 index 0000000000..3625bccc3e --- /dev/null +++ b/modules/angular2/src/transform/template_compiler/compile_step_factory.dart @@ -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 createSteps( + ViewDefinition template, List subTaskPromises) { + // TODO(kegluneq): Add other compile steps from default_steps.dart. + return [ + new ViewSplitter(_parser), + new PropertyBindingParser(_parser), + new TextInterpolationParser(_parser) + ]; + } +} diff --git a/modules/angular2/src/transform/template_compiler/generator.dart b/modules/angular2/src/transform/template_compiler/generator.dart index 97bd2cb452..29d7a95df7 100644 --- a/modules/angular2/src/transform/template_compiler/generator.dart +++ b/modules/angular2/src/transform/template_compiler/generator.dart @@ -5,22 +5,22 @@ import 'dart:async'; import 'package:analyzer/analyzer.dart'; 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/render/api.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/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'; -import 'package:angular2/src/dom/dom_adapter.dart'; +import 'package:angular2/src/render/dom/compiler/template_loader.dart'; +import "package:angular2/src/services/xhr.dart" show XHR; 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/logging.dart'; import 'package:angular2/src/transform/common/names.dart'; import 'package:angular2/src/transform/common/parser.dart'; import 'package:angular2/src/transform/common/property_utils.dart' as prop; import 'package:barback/barback.dart'; -import 'package:code_transformers/assets.dart'; +import 'compile_step_factory.dart'; import 'recording_reflection_capabilities.dart'; +import 'xhr_impl.dart'; /// Reads the `.ng_deps.dart` file represented by `entryPoint` and parses any /// Angular 2 `View` annotations it declares to generate `getter`s, @@ -30,28 +30,27 @@ import 'recording_reflection_capabilities.dart'; Future processTemplates(AssetReader reader, AssetId entryPoint) async { var parser = new Parser(reader); NgDeps ngDeps = await parser.parse(entryPoint); - var extractor = new _TemplateExtractor(reader, entryPoint); + var extractor = new _TemplateExtractor(new XhrImpl(reader, entryPoint)); var registrations = new StringBuffer(); for (var rType in ngDeps.registeredTypes) { - (await extractor.extractTemplates(rType)) - .forEach((RecordingReflectionCapabilities values) { - var calls = _generateGetters('${rType.typeName}', values.getterNames); - if (calls.isNotEmpty) { - registrations.write('..${REGISTER_GETTERS_METHOD_NAME}' - '({${calls.join(', ')}})'); - } - calls = _generateSetters('${rType.typeName}', values.setterNames); - if (calls.isNotEmpty) { - registrations.write('..${REGISTER_SETTERS_METHOD_NAME}' - '({${calls.join(', ')}})'); - } - calls = _generateMethods('${rType.typeName}', values.methodNames); - if (calls.isNotEmpty) { - registrations.write('..${REGISTER_METHODS_METHOD_NAME}' - '({${calls.join(', ')}})'); - } - }); + var values = await extractor.extractTemplates(rType); + if (values == null) continue; + var calls = _generateGetters('${rType.typeName}', values.getterNames); + if (calls.isNotEmpty) { + registrations.write('..${REGISTER_GETTERS_METHOD_NAME}' + '({${calls.join(', ')}})'); + } + calls = _generateSetters('${rType.typeName}', values.setterNames); + if (calls.isNotEmpty) { + registrations.write('..${REGISTER_SETTERS_METHOD_NAME}' + '({${calls.join(', ')}})'); + } + calls = _generateMethods('${rType.typeName}', values.methodNames); + if (calls.isNotEmpty) { + registrations.write('..${REGISTER_METHODS_METHOD_NAME}' + '({${calls.join(', ')}})'); + } } var code = ngDeps.code; @@ -105,75 +104,52 @@ Iterable _generateMethods( /// template code if necessary, and determines what values will be /// reflectively accessed from that template. class _TemplateExtractor { - final AssetReader _reader; - final AssetId _entryPoint; - final CompilePipeline _pipeline; + final CompileStepFactory _factory; final _TemplateExtractVisitor _visitor = new _TemplateExtractVisitor(); + final TemplateLoader _loader; - _TemplateExtractor(this._reader, this._entryPoint) - : _pipeline = new CompilePipeline(_createCompileSteps()); + _TemplateExtractor(XHR xhr) + : _loader = new TemplateLoader(xhr, new UrlResolver()), + _factory = new CompileStepFactory(new ng.Parser(new ng.Lexer())); - static List _createCompileSteps() { - var parser = new ng.Parser(new ng.Lexer()); - // TODO(kegluneq): Add other compile steps from default_steps.dart. - return [ - new ViewSplitter(parser), - new PropertyBindingParser(parser), - new TextInterpolationParser(parser) - ]; + Future extractTemplates(RegisteredType t) { + return _processTemplate(_processRegisteredType(t)); } - Future> extractTemplates( - RegisteredType t) async { - return (await _processRegisteredType(t)).map(_processTemplate).toList(); - } + Future _processTemplate( + ViewDefinition viewDef) async { + // Check for "imperative views". + if (viewDef.template == null && viewDef.absUrl == null) return null; - RecordingReflectionCapabilities _processTemplate(String templateCode) { var recordingCapabilities = new RecordingReflectionCapabilities(); var savedReflectionCapabilities = reflector.reflectionCapabilities; 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; return recordingCapabilities; } - Future> _processRegisteredType(RegisteredType t) async { + ViewDefinition _processRegisteredType(RegisteredType t) { _visitor.reset(); t.annotations.accept(_visitor); - var toReturn = _visitor.inlineValues; - 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 _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); + return _visitor.viewDef; } } /// 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 { - final List inlineValues = []; - final List urlValues = []; + ViewDefinition viewDef = new ViewDefinition(); void reset() { - inlineValues.clear(); - urlValues.clear(); + viewDef = new ViewDefinition(); } @override @@ -195,9 +171,16 @@ class _TemplateExtractVisitor extends Object with RecursiveAstVisitor { } var valueString = stringLiteralToString(node.expression); if (keyString == 'templateUrl') { - urlValues.add(valueString); + if (viewDef.absUrl != null) { + logger.error( + 'Found multiple values for "templateUrl". Source: ${node}'); + } + viewDef.absUrl = valueString; } else { - inlineValues.add(valueString); + if (viewDef.template != null) { + logger.error('Found multiple values for "template". Source: ${node}'); + } + viewDef.template = valueString; } } return null; diff --git a/modules/angular2/src/transform/template_compiler/xhr_impl.dart b/modules/angular2/src/transform/template_compiler/xhr_impl.dart new file mode 100644 index 0000000000..055f04329f --- /dev/null +++ b/modules/angular2/src/transform/template_compiler/xhr_impl.dart @@ -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 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); + } +}