2015-03-20 15:37:09 -07:00
|
|
|
library angular2.transform.template_compiler.generator;
|
2015-03-13 13:24:56 -07:00
|
|
|
|
|
|
|
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;
|
2015-05-01 17:29:58 -07:00
|
|
|
import 'package:angular2/src/render/api.dart';
|
2015-04-02 14:40:49 -07:00
|
|
|
import 'package:angular2/src/render/dom/compiler/compile_pipeline.dart';
|
2015-05-01 17:29:58 -07:00
|
|
|
import 'package:angular2/src/render/dom/compiler/template_loader.dart';
|
|
|
|
import "package:angular2/src/services/xhr.dart" show XHR;
|
2015-03-13 13:24:56 -07:00
|
|
|
import 'package:angular2/src/reflection/reflection.dart';
|
2015-05-01 17:29:58 -07:00
|
|
|
import 'package:angular2/src/services/url_resolver.dart';
|
2015-03-13 13:24:56 -07:00
|
|
|
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';
|
2015-04-09 17:51:06 -07:00
|
|
|
import 'package:angular2/src/transform/common/property_utils.dart' as prop;
|
2015-03-13 13:24:56 -07:00
|
|
|
import 'package:barback/barback.dart';
|
|
|
|
|
2015-05-01 17:29:58 -07:00
|
|
|
import 'compile_step_factory.dart';
|
2015-03-13 13:24:56 -07:00
|
|
|
import 'recording_reflection_capabilities.dart';
|
2015-05-01 17:29:58 -07:00
|
|
|
import 'xhr_impl.dart';
|
2015-03-13 13:24:56 -07:00
|
|
|
|
2015-03-13 13:55:49 -07:00
|
|
|
/// Reads the `.ng_deps.dart` file represented by `entryPoint` and parses any
|
2015-04-09 21:20:11 +02:00
|
|
|
/// Angular 2 `View` annotations it declares to generate `getter`s,
|
2015-03-13 13:55:49 -07:00
|
|
|
/// `setter`s, and `method`s that would otherwise be reflectively accessed.
|
|
|
|
///
|
2015-04-17 13:01:07 -07:00
|
|
|
/// This method assumes a {@link DomAdapter} has been registered.
|
2015-03-13 13:24:56 -07:00
|
|
|
Future<String> processTemplates(AssetReader reader, AssetId entryPoint) async {
|
|
|
|
var parser = new Parser(reader);
|
|
|
|
NgDeps ngDeps = await parser.parse(entryPoint);
|
2015-05-01 17:29:58 -07:00
|
|
|
var extractor = new _TemplateExtractor(new XhrImpl(reader, entryPoint));
|
2015-03-13 13:24:56 -07:00
|
|
|
|
|
|
|
var registrations = new StringBuffer();
|
2015-03-19 09:16:01 -07:00
|
|
|
for (var rType in ngDeps.registeredTypes) {
|
2015-05-01 17:29:58 -07:00
|
|
|
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(', ')}})');
|
|
|
|
}
|
2015-03-19 09:16:01 -07:00
|
|
|
}
|
2015-03-13 13:24:56 -07:00
|
|
|
|
2015-03-13 13:55:49 -07:00
|
|
|
var code = ngDeps.code;
|
2015-03-13 13:24:56 -07:00
|
|
|
if (registrations.length == 0) return code;
|
|
|
|
var codeInjectIdx = ngDeps.registeredTypes.last.registerMethod.end;
|
|
|
|
return '${code.substring(0, codeInjectIdx)}'
|
|
|
|
'${registrations}'
|
|
|
|
'${code.substring(codeInjectIdx)}';
|
|
|
|
}
|
|
|
|
|
2015-04-24 10:28:09 -07:00
|
|
|
Iterable<String> _generateGetters(
|
|
|
|
String typeName, Iterable<String> getterNames) {
|
2015-03-19 09:16:01 -07:00
|
|
|
// TODO(kegluneq): Include `typeName` where possible.
|
2015-04-09 17:51:06 -07:00
|
|
|
return getterNames.map((getterName) {
|
|
|
|
if (!prop.isValid(getterName)) {
|
|
|
|
// TODO(kegluenq): Eagerly throw here once #1295 is addressed.
|
|
|
|
return prop.lazyInvalidGetter(getterName);
|
|
|
|
} else {
|
|
|
|
return ''' '${prop.sanitize(getterName)}': (o) => o.$getterName''';
|
|
|
|
}
|
|
|
|
});
|
2015-03-13 13:55:49 -07:00
|
|
|
}
|
|
|
|
|
2015-04-24 10:28:09 -07:00
|
|
|
Iterable<String> _generateSetters(
|
|
|
|
String typeName, Iterable<String> setterName) {
|
2015-04-09 17:51:06 -07:00
|
|
|
return setterName.map((setterName) {
|
|
|
|
if (!prop.isValid(setterName)) {
|
|
|
|
// TODO(kegluenq): Eagerly throw here once #1295 is addressed.
|
|
|
|
return prop.lazyInvalidSetter(setterName);
|
|
|
|
} else {
|
|
|
|
return ''' '${prop.sanitize(setterName)}': '''
|
|
|
|
''' (o, v) => o.$setterName = v ''';
|
|
|
|
}
|
|
|
|
});
|
2015-03-13 13:55:49 -07:00
|
|
|
}
|
|
|
|
|
2015-04-24 10:28:09 -07:00
|
|
|
Iterable<String> _generateMethods(
|
|
|
|
String typeName, Iterable<String> methodNames) {
|
2015-04-09 17:51:06 -07:00
|
|
|
return methodNames.map((methodName) {
|
|
|
|
if (!prop.isValid(methodName)) {
|
|
|
|
// TODO(kegluenq): Eagerly throw here once #1295 is addressed.
|
|
|
|
return prop.lazyInvalidMethod(methodName);
|
|
|
|
} else {
|
|
|
|
return ''' '${prop.sanitize(methodName)}': '''
|
|
|
|
'(o, List args) => Function.apply(o.$methodName, args) ';
|
|
|
|
}
|
|
|
|
});
|
2015-03-13 13:55:49 -07:00
|
|
|
}
|
|
|
|
|
2015-04-09 21:20:11 +02:00
|
|
|
/// Extracts `template` and `url` values from `View` annotations, reads
|
2015-03-19 09:16:01 -07:00
|
|
|
/// template code if necessary, and determines what values will be
|
|
|
|
/// reflectively accessed from that template.
|
|
|
|
class _TemplateExtractor {
|
2015-05-01 17:29:58 -07:00
|
|
|
final CompileStepFactory _factory;
|
2015-03-19 09:16:01 -07:00
|
|
|
final _TemplateExtractVisitor _visitor = new _TemplateExtractVisitor();
|
2015-05-01 17:29:58 -07:00
|
|
|
final TemplateLoader _loader;
|
2015-03-19 09:16:01 -07:00
|
|
|
|
2015-05-01 17:29:58 -07:00
|
|
|
_TemplateExtractor(XHR xhr)
|
|
|
|
: _loader = new TemplateLoader(xhr, new UrlResolver()),
|
|
|
|
_factory = new CompileStepFactory(new ng.Parser(new ng.Lexer()));
|
2015-03-13 13:24:56 -07:00
|
|
|
|
2015-05-01 17:29:58 -07:00
|
|
|
Future<RecordingReflectionCapabilities> extractTemplates(RegisteredType t) {
|
|
|
|
return _processTemplate(_processRegisteredType(t));
|
2015-03-19 09:16:01 -07:00
|
|
|
}
|
2015-03-13 13:24:56 -07:00
|
|
|
|
2015-05-01 17:29:58 -07:00
|
|
|
Future<RecordingReflectionCapabilities> _processTemplate(
|
|
|
|
ViewDefinition viewDef) async {
|
|
|
|
// Check for "imperative views".
|
|
|
|
if (viewDef.template == null && viewDef.absUrl == null) return null;
|
|
|
|
|
2015-03-19 09:16:01 -07:00
|
|
|
var recordingCapabilities = new RecordingReflectionCapabilities();
|
|
|
|
var savedReflectionCapabilities = reflector.reflectionCapabilities;
|
|
|
|
reflector.reflectionCapabilities = recordingCapabilities;
|
2015-03-13 13:24:56 -07:00
|
|
|
|
2015-05-01 17:29:58 -07:00
|
|
|
// 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');
|
2015-03-19 09:16:01 -07:00
|
|
|
|
|
|
|
reflector.reflectionCapabilities = savedReflectionCapabilities;
|
|
|
|
return recordingCapabilities;
|
|
|
|
}
|
|
|
|
|
2015-05-01 17:29:58 -07:00
|
|
|
ViewDefinition _processRegisteredType(RegisteredType t) {
|
2015-03-19 09:16:01 -07:00
|
|
|
_visitor.reset();
|
|
|
|
t.annotations.accept(_visitor);
|
2015-05-01 17:29:58 -07:00
|
|
|
return _visitor.viewDef;
|
2015-03-19 09:16:01 -07:00
|
|
|
}
|
2015-03-13 13:24:56 -07:00
|
|
|
}
|
|
|
|
|
2015-03-13 13:55:49 -07:00
|
|
|
/// Visitor responsible for processing the `annotations` property of a
|
2015-05-01 17:29:58 -07:00
|
|
|
/// {@link RegisterType} object and pulling out template information.
|
2015-03-13 13:24:56 -07:00
|
|
|
class _TemplateExtractVisitor extends Object with RecursiveAstVisitor<Object> {
|
2015-05-01 17:29:58 -07:00
|
|
|
ViewDefinition viewDef = new ViewDefinition();
|
2015-03-13 13:24:56 -07:00
|
|
|
|
2015-03-19 09:16:01 -07:00
|
|
|
void reset() {
|
2015-05-01 17:29:58 -07:00
|
|
|
viewDef = new ViewDefinition();
|
2015-03-19 09:16:01 -07:00
|
|
|
}
|
2015-03-13 13:24:56 -07:00
|
|
|
|
|
|
|
@override
|
|
|
|
Object visitNamedExpression(NamedExpression node) {
|
|
|
|
// TODO(kegluneq): Remove this limitation.
|
2015-03-19 09:16:01 -07:00
|
|
|
if (node.name is! Label || node.name.label is! SimpleIdentifier) {
|
2015-03-13 13:24:56 -07:00
|
|
|
logger.error(
|
2015-04-01 13:47:42 -07:00
|
|
|
'Angular 2 currently only supports simple identifiers in directives.'
|
2015-03-13 13:24:56 -07:00
|
|
|
' Source: ${node}');
|
2015-03-19 09:16:01 -07:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
var keyString = '${node.name.label}';
|
2015-04-09 21:20:11 +02:00
|
|
|
if (keyString == 'template' || keyString == 'templateUrl') {
|
2015-03-19 09:16:01 -07:00
|
|
|
if (node.expression is! SimpleStringLiteral) {
|
|
|
|
logger.error(
|
2015-04-01 13:47:42 -07:00
|
|
|
'Angular 2 currently only supports string literals in directives.'
|
2015-03-19 09:16:01 -07:00
|
|
|
' Source: ${node}');
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
var valueString = stringLiteralToString(node.expression);
|
2015-04-09 21:20:11 +02:00
|
|
|
if (keyString == 'templateUrl') {
|
2015-05-01 17:29:58 -07:00
|
|
|
if (viewDef.absUrl != null) {
|
|
|
|
logger.error(
|
|
|
|
'Found multiple values for "templateUrl". Source: ${node}');
|
|
|
|
}
|
|
|
|
viewDef.absUrl = valueString;
|
2015-03-19 09:16:01 -07:00
|
|
|
} else {
|
2015-05-01 17:29:58 -07:00
|
|
|
if (viewDef.template != null) {
|
|
|
|
logger.error('Found multiple values for "template". Source: ${node}');
|
|
|
|
}
|
|
|
|
viewDef.template = valueString;
|
2015-03-19 09:16:01 -07:00
|
|
|
}
|
2015-03-13 13:24:56 -07:00
|
|
|
}
|
2015-03-19 09:16:01 -07:00
|
|
|
return null;
|
2015-03-13 13:24:56 -07:00
|
|
|
}
|
|
|
|
}
|