feat(dart/transform): Use the render Compiler and the DirectiveParser
Update the `TemplateCompile` step to use the full render `Compiler`. Provide `DirectiveMetadata` for `ViewDefinition` objects and use it to run the `DirectiveParser` step of the render compile pipeline.
This commit is contained in:
parent
401c9efad7
commit
44f829dbc6
|
@ -83,7 +83,16 @@ class Html5LibDomAdapter implements DomAdapter {
|
||||||
throw 'not implemented';
|
throw 'not implemented';
|
||||||
}
|
}
|
||||||
String nodeName(node) {
|
String nodeName(node) {
|
||||||
throw 'not implemented';
|
switch (node.nodeType) {
|
||||||
|
case Node.ELEMENT_NODE:
|
||||||
|
return (node as Element).localName;
|
||||||
|
case Node.TEXT_NODE:
|
||||||
|
return '#text';
|
||||||
|
default:
|
||||||
|
throw 'not implemented for type ${node.nodeType}. '
|
||||||
|
'See http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-1950641247'
|
||||||
|
' for node types definitions.';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
String nodeValue(node) => node.data;
|
String nodeValue(node) => node.data;
|
||||||
String type(node) {
|
String type(node) {
|
||||||
|
@ -179,9 +188,8 @@ class Html5LibDomAdapter implements DomAdapter {
|
||||||
getElementsByTagName(element, String name) {
|
getElementsByTagName(element, String name) {
|
||||||
throw 'not implemented';
|
throw 'not implemented';
|
||||||
}
|
}
|
||||||
List classList(element) {
|
List classList(element) => element.classes.toList();
|
||||||
throw 'not implemented';
|
|
||||||
}
|
|
||||||
addClass(element, String classname) {
|
addClass(element, String classname) {
|
||||||
element.classes.add(classname);
|
element.classes.add(classname);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,9 @@ import 'parser.dart';
|
||||||
DirectiveMetadata readDirectiveMetadata(RegisteredType t) {
|
DirectiveMetadata readDirectiveMetadata(RegisteredType t) {
|
||||||
var visitor = new _DirectiveMetadataVisitor();
|
var visitor = new _DirectiveMetadataVisitor();
|
||||||
t.annotations.accept(visitor);
|
t.annotations.accept(visitor);
|
||||||
|
if (visitor.meta != null) {
|
||||||
|
visitor.meta.id = '${t.typeName}';
|
||||||
|
}
|
||||||
return visitor.meta;
|
return visitor.meta;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,8 @@ class NgDeps {
|
||||||
final List<ImportDirective> imports = [];
|
final List<ImportDirective> imports = [];
|
||||||
final List<ExportDirective> exports = [];
|
final List<ExportDirective> exports = [];
|
||||||
final List<RegisteredType> registeredTypes = [];
|
final List<RegisteredType> registeredTypes = [];
|
||||||
FunctionDeclaration setupMethod;
|
LibraryDirective lib = null;
|
||||||
|
FunctionDeclaration setupMethod = null;
|
||||||
|
|
||||||
NgDeps(this.code);
|
NgDeps(this.code);
|
||||||
}
|
}
|
||||||
|
@ -76,6 +77,12 @@ class NgDeps {
|
||||||
class _ParseNgDepsVisitor extends Object with RecursiveAstVisitor<Object> {
|
class _ParseNgDepsVisitor extends Object with RecursiveAstVisitor<Object> {
|
||||||
NgDeps ngDeps = null;
|
NgDeps ngDeps = null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object visitLibraryDirective(LibraryDirective node) {
|
||||||
|
ngDeps.lib = node;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Object visitImportDirective(ImportDirective node) {
|
Object visitImportDirective(ImportDirective node) {
|
||||||
ngDeps.imports.add(node);
|
ngDeps.imports.add(node);
|
||||||
|
|
|
@ -6,6 +6,7 @@ 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.dart';
|
||||||
import 'package:angular2/src/render/dom/compiler/compile_step_factory.dart'
|
import 'package:angular2/src/render/dom/compiler/compile_step_factory.dart'
|
||||||
as base;
|
as base;
|
||||||
|
import 'package:angular2/src/render/dom/compiler/directive_parser.dart';
|
||||||
import 'package:angular2/src/render/dom/compiler/property_binding_parser.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/text_interpolation_parser.dart';
|
||||||
import 'package:angular2/src/render/dom/compiler/view_splitter.dart';
|
import 'package:angular2/src/render/dom/compiler/view_splitter.dart';
|
||||||
|
@ -20,6 +21,7 @@ class CompileStepFactory implements base.CompileStepFactory {
|
||||||
return [
|
return [
|
||||||
new ViewSplitter(_parser),
|
new ViewSplitter(_parser),
|
||||||
new PropertyBindingParser(_parser),
|
new PropertyBindingParser(_parser),
|
||||||
|
new DirectiveParser(_parser, template.directives),
|
||||||
new TextInterpolationParser(_parser)
|
new TextInterpolationParser(_parser)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,24 +2,22 @@ library angular2.transform.template_compiler.generator;
|
||||||
|
|
||||||
import 'dart:async';
|
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/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/api.dart';
|
||||||
import 'package:angular2/src/render/dom/compiler/compile_pipeline.dart';
|
import 'package:angular2/src/render/dom/compiler/compiler.dart';
|
||||||
import 'package:angular2/src/render/dom/compiler/template_loader.dart';
|
import 'package:angular2/src/render/dom/compiler/template_loader.dart';
|
||||||
import "package:angular2/src/services/xhr.dart" show XHR;
|
import "package:angular2/src/services/xhr.dart" show XHR;
|
||||||
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/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/names.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:angular2/src/transform/common/property_utils.dart' as prop;
|
||||||
import 'package:barback/barback.dart';
|
import 'package:barback/barback.dart';
|
||||||
|
|
||||||
import 'compile_step_factory.dart';
|
import 'compile_step_factory.dart';
|
||||||
import 'recording_reflection_capabilities.dart';
|
import 'recording_reflection_capabilities.dart';
|
||||||
|
import 'view_definition_creator.dart';
|
||||||
import 'xhr_impl.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
|
||||||
|
@ -28,42 +26,40 @@ import 'xhr_impl.dart';
|
||||||
///
|
///
|
||||||
/// This method assumes a {@link DomAdapter} has been registered.
|
/// This method assumes a {@link DomAdapter} has been registered.
|
||||||
Future<String> processTemplates(AssetReader reader, AssetId entryPoint) async {
|
Future<String> processTemplates(AssetReader reader, AssetId entryPoint) async {
|
||||||
var parser = new Parser(reader);
|
var viewDefResults = await createViewDefinitions(reader, entryPoint);
|
||||||
NgDeps ngDeps = await parser.parse(entryPoint);
|
|
||||||
var extractor = new _TemplateExtractor(new XhrImpl(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 viewDef in viewDefResults.viewDefinitions.values) {
|
||||||
var values = await extractor.extractTemplates(rType);
|
var values = await extractor.extractTemplates(viewDef);
|
||||||
if (values == null) continue;
|
if (values == null) continue;
|
||||||
var calls = _generateGetters('${rType.typeName}', values.getterNames);
|
var calls = _generateGetters(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(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(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 = viewDefResults.ngDeps.code;
|
||||||
if (registrations.length == 0) return code;
|
if (registrations.length == 0) return code;
|
||||||
var codeInjectIdx = ngDeps.registeredTypes.last.registerMethod.end;
|
var codeInjectIdx =
|
||||||
|
viewDefResults.ngDeps.registeredTypes.last.registerMethod.end;
|
||||||
return '${code.substring(0, codeInjectIdx)}'
|
return '${code.substring(0, codeInjectIdx)}'
|
||||||
'${registrations}'
|
'${registrations}'
|
||||||
'${code.substring(codeInjectIdx)}';
|
'${code.substring(codeInjectIdx)}';
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterable<String> _generateGetters(
|
Iterable<String> _generateGetters(Iterable<String> getterNames) {
|
||||||
String typeName, Iterable<String> getterNames) {
|
|
||||||
// TODO(kegluneq): Include `typeName` where possible.
|
|
||||||
return getterNames.map((getterName) {
|
return getterNames.map((getterName) {
|
||||||
if (!prop.isValid(getterName)) {
|
if (!prop.isValid(getterName)) {
|
||||||
// TODO(kegluenq): Eagerly throw here once #1295 is addressed.
|
// TODO(kegluenq): Eagerly throw here once #1295 is addressed.
|
||||||
|
@ -74,8 +70,7 @@ Iterable<String> _generateGetters(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterable<String> _generateSetters(
|
Iterable<String> _generateSetters(Iterable<String> setterName) {
|
||||||
String typeName, Iterable<String> setterName) {
|
|
||||||
return setterName.map((setterName) {
|
return setterName.map((setterName) {
|
||||||
if (!prop.isValid(setterName)) {
|
if (!prop.isValid(setterName)) {
|
||||||
// TODO(kegluenq): Eagerly throw here once #1295 is addressed.
|
// TODO(kegluenq): Eagerly throw here once #1295 is addressed.
|
||||||
|
@ -87,8 +82,7 @@ Iterable<String> _generateSetters(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterable<String> _generateMethods(
|
Iterable<String> _generateMethods(Iterable<String> methodNames) {
|
||||||
String typeName, Iterable<String> methodNames) {
|
|
||||||
return methodNames.map((methodName) {
|
return methodNames.map((methodName) {
|
||||||
if (!prop.isValid(methodName)) {
|
if (!prop.isValid(methodName)) {
|
||||||
// TODO(kegluenq): Eagerly throw here once #1295 is addressed.
|
// TODO(kegluenq): Eagerly throw here once #1295 is addressed.
|
||||||
|
@ -104,85 +98,25 @@ 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 CompileStepFactory _factory;
|
final RenderCompiler _compiler;
|
||||||
final _TemplateExtractVisitor _visitor = new _TemplateExtractVisitor();
|
|
||||||
final TemplateLoader _loader;
|
|
||||||
|
|
||||||
_TemplateExtractor(XHR xhr)
|
_TemplateExtractor(XHR xhr) : _compiler = new DomCompiler(
|
||||||
: _loader = new TemplateLoader(xhr, new UrlResolver()),
|
new CompileStepFactory(new ng.Parser(new ng.Lexer())),
|
||||||
_factory = new CompileStepFactory(new ng.Parser(new ng.Lexer()));
|
new TemplateLoader(xhr, new UrlResolver()));
|
||||||
|
|
||||||
Future<RecordingReflectionCapabilities> extractTemplates(RegisteredType t) {
|
Future<RecordingReflectionCapabilities> extractTemplates(
|
||||||
return _processTemplate(_processRegisteredType(t));
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<RecordingReflectionCapabilities> _processTemplate(
|
|
||||||
ViewDefinition viewDef) async {
|
ViewDefinition viewDef) async {
|
||||||
// Check for "imperative views".
|
// Check for "imperative views".
|
||||||
if (viewDef.template == null && viewDef.absUrl == null) return null;
|
if (viewDef.template == null && viewDef.absUrl == null) return null;
|
||||||
|
|
||||||
var recordingCapabilities = new RecordingReflectionCapabilities();
|
|
||||||
var savedReflectionCapabilities = reflector.reflectionCapabilities;
|
var savedReflectionCapabilities = reflector.reflectionCapabilities;
|
||||||
|
var recordingCapabilities = new RecordingReflectionCapabilities();
|
||||||
reflector.reflectionCapabilities = recordingCapabilities;
|
reflector.reflectionCapabilities = recordingCapabilities;
|
||||||
|
|
||||||
// TODO(kegluneq): Rewrite url to inline `template` where possible.
|
// TODO(kegluneq): Rewrite url to inline `template` where possible.
|
||||||
// See [https://github.com/angular/angular/issues/1035].
|
await _compiler.compile(viewDef);
|
||||||
var domNode = await _loader.load(viewDef);
|
|
||||||
|
|
||||||
new CompilePipeline(_factory.createSteps(viewDef, [])).process(
|
|
||||||
domNode, '$domNode');
|
|
||||||
|
|
||||||
reflector.reflectionCapabilities = savedReflectionCapabilities;
|
reflector.reflectionCapabilities = savedReflectionCapabilities;
|
||||||
return recordingCapabilities;
|
return recordingCapabilities;
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewDefinition _processRegisteredType(RegisteredType t) {
|
|
||||||
_visitor.reset();
|
|
||||||
t.annotations.accept(_visitor);
|
|
||||||
return _visitor.viewDef;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Visitor responsible for processing the `annotations` property of a
|
|
||||||
/// {@link RegisterType} object and pulling out template information.
|
|
||||||
class _TemplateExtractVisitor extends Object with RecursiveAstVisitor<Object> {
|
|
||||||
ViewDefinition viewDef = new ViewDefinition();
|
|
||||||
|
|
||||||
void reset() {
|
|
||||||
viewDef = new ViewDefinition();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Object visitNamedExpression(NamedExpression node) {
|
|
||||||
// TODO(kegluneq): Remove this limitation.
|
|
||||||
if (node.name is! Label || node.name.label is! SimpleIdentifier) {
|
|
||||||
logger.error(
|
|
||||||
'Angular 2 currently only supports simple identifiers in directives.'
|
|
||||||
' Source: ${node}');
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
var keyString = '${node.name.label}';
|
|
||||||
if (keyString == 'template' || keyString == 'templateUrl') {
|
|
||||||
if (node.expression is! SimpleStringLiteral) {
|
|
||||||
logger.error(
|
|
||||||
'Angular 2 currently only supports string literals in directives.'
|
|
||||||
' Source: ${node}');
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
var valueString = stringLiteralToString(node.expression);
|
|
||||||
if (keyString == 'templateUrl') {
|
|
||||||
if (viewDef.absUrl != null) {
|
|
||||||
logger.error(
|
|
||||||
'Found multiple values for "templateUrl". Source: ${node}');
|
|
||||||
}
|
|
||||||
viewDef.absUrl = valueString;
|
|
||||||
} else {
|
|
||||||
if (viewDef.template != null) {
|
|
||||||
logger.error('Found multiple values for "template". Source: ${node}');
|
|
||||||
}
|
|
||||||
viewDef.template = valueString;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,232 @@
|
||||||
|
library angular2.transform.template_compiler.view_definition_creator;
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:analyzer/analyzer.dart';
|
||||||
|
import 'package:angular2/src/render/api.dart';
|
||||||
|
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';
|
||||||
|
import 'package:angular2/src/transform/common/names.dart';
|
||||||
|
import 'package:angular2/src/transform/common/parser.dart';
|
||||||
|
import 'package:barback/barback.dart';
|
||||||
|
import 'package:code_transformers/assets.dart';
|
||||||
|
|
||||||
|
/// Creates [ViewDefinition] objects for all `View` `Directive`s defined in
|
||||||
|
/// `entryPoint`.
|
||||||
|
Future<ViewDefinitionResults> createViewDefinitions(
|
||||||
|
AssetReader reader, AssetId entryPoint) async {
|
||||||
|
return await new _ViewDefinitionCreator(reader, entryPoint).createViewDefs();
|
||||||
|
}
|
||||||
|
|
||||||
|
class ViewDefinitionResults {
|
||||||
|
final NgDeps ngDeps;
|
||||||
|
final Map<RegisteredType, ViewDefinition> viewDefinitions;
|
||||||
|
ViewDefinitionResults._(this.ngDeps, this.viewDefinitions);
|
||||||
|
}
|
||||||
|
|
||||||
|
String _getComponentId(AssetId assetId, String className) =>
|
||||||
|
'$assetId:$className';
|
||||||
|
|
||||||
|
/// Creates [ViewDefinition] objects for all `View` `Directive`s defined in
|
||||||
|
/// `entryPoint`.
|
||||||
|
class _ViewDefinitionCreator {
|
||||||
|
final AssetReader reader;
|
||||||
|
final AssetId entryPoint;
|
||||||
|
final Future<NgDeps> ngDepsFuture;
|
||||||
|
|
||||||
|
_ViewDefinitionCreator(AssetReader reader, AssetId entryPoint)
|
||||||
|
: this.reader = reader,
|
||||||
|
this.entryPoint = entryPoint,
|
||||||
|
ngDepsFuture = new Parser(reader).parse(entryPoint);
|
||||||
|
|
||||||
|
Future<ViewDefinitionResults> createViewDefs() async {
|
||||||
|
var ngDeps = await ngDepsFuture;
|
||||||
|
|
||||||
|
var retVal = <RegisteredType, ViewDefinition>{};
|
||||||
|
var visitor = new _TemplateExtractVisitor(await _createMetadataMap());
|
||||||
|
ngDeps.registeredTypes.forEach((rType) {
|
||||||
|
visitor.reset();
|
||||||
|
rType.annotations.accept(visitor);
|
||||||
|
if (visitor.viewDef != null) {
|
||||||
|
var typeName = '${rType.typeName}';
|
||||||
|
if (visitor._metadataMap.containsKey(typeName)) {
|
||||||
|
visitor.viewDef.componentId = visitor._metadataMap[typeName].id;
|
||||||
|
} else {
|
||||||
|
logger.warning('Missing component "$typeName" in metadata map',
|
||||||
|
asset: entryPoint);
|
||||||
|
visitor.viewDef.componentId = _getComponentId(entryPoint, typeName);
|
||||||
|
}
|
||||||
|
retVal[rType] = visitor.viewDef;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return new ViewDefinitionResults._(ngDeps, retVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a map from [AssetId] to import prefix for `.dart` libraries
|
||||||
|
/// imported by `entryPoint`, excluding any `.ng_deps.dart` files it imports.
|
||||||
|
/// Unprefixed imports have `null` as their value. `entryPoint` is included
|
||||||
|
/// in the map with no prefix.
|
||||||
|
Future<Map<AssetId, String>> _createImportAssetToPrefixMap() async {
|
||||||
|
// TODO(kegluneq): Support `part` directives.
|
||||||
|
var ngDeps = await ngDepsFuture;
|
||||||
|
|
||||||
|
var importAssetToPrefix = <AssetId, String>{};
|
||||||
|
// Include the `.ng_meta.dart` file associated with `entryPoint`.
|
||||||
|
importAssetToPrefix[new AssetId(
|
||||||
|
entryPoint.package, toMetaExtension(entryPoint.path))] = null;
|
||||||
|
|
||||||
|
for (ImportDirective node in ngDeps.imports) {
|
||||||
|
var uri = stringLiteralToString(node.uri);
|
||||||
|
if (uri.endsWith('.dart') && !uri.endsWith(DEPS_EXTENSION)) {
|
||||||
|
var prefix = node.prefix != null && node.prefix.name != null
|
||||||
|
? '${node.prefix.name}'
|
||||||
|
: null;
|
||||||
|
importAssetToPrefix[
|
||||||
|
uriToAssetId(entryPoint, uri, logger, null /* span */)] = prefix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return importAssetToPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads the `.ng_meta.json` files associated with all of `entryPoint`'s
|
||||||
|
/// imports and creates a map `Type` name, prefixed if appropriate to the
|
||||||
|
/// associated [DirectiveMetadata].
|
||||||
|
///
|
||||||
|
/// For example, if in `entryPoint` we have:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// import 'component.dart' as prefix;
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// and in 'component.dart' we have:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// @Component(...)
|
||||||
|
/// class MyComponent {...}
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// This method will look for `component.ng_meta.json`to contain the
|
||||||
|
/// serialized [DirectiveMetadata] `MyComponent` and any other `Directive`s
|
||||||
|
/// declared in `component.dart`. We use this information to build a map:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// {
|
||||||
|
/// "prefix.MyComponent": [DirectiveMetadata for MyComponent],
|
||||||
|
/// ...<any other entries>...
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
Future<Map<String, DirectiveMetadata>> _createMetadataMap() async {
|
||||||
|
var importAssetToPrefix = await _createImportAssetToPrefixMap();
|
||||||
|
|
||||||
|
var retVal = <String, DirectiveMetadata>{};
|
||||||
|
for (var importAssetId in importAssetToPrefix.keys) {
|
||||||
|
var metaAssetId = new AssetId(
|
||||||
|
importAssetId.package, toMetaExtension(importAssetId.path));
|
||||||
|
if (await reader.hasInput(metaAssetId)) {
|
||||||
|
try {
|
||||||
|
var json = await reader.readAsString(metaAssetId);
|
||||||
|
var jsonMap = JSON.decode(json);
|
||||||
|
jsonMap.forEach((className, metaDataMap) {
|
||||||
|
var prefixStr = importAssetToPrefix[importAssetId];
|
||||||
|
var key = prefixStr != null ? '$prefixStr.$className' : className;
|
||||||
|
var value = directiveMetadataFromMap(metaDataMap)
|
||||||
|
..id = _getComponentId(importAssetId, className);
|
||||||
|
retVal[key] = value;
|
||||||
|
});
|
||||||
|
} catch (ex, stackTrace) {
|
||||||
|
logger.warning('Failed to decode: $ex, $stackTrace',
|
||||||
|
asset: metaAssetId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Visitor responsible for processing the `annotations` property of a
|
||||||
|
/// {@link RegisterType} object and pulling out [ViewDefinition] information.
|
||||||
|
class _TemplateExtractVisitor extends Object with RecursiveAstVisitor<Object> {
|
||||||
|
ViewDefinition viewDef = null;
|
||||||
|
final Map<String, DirectiveMetadata> _metadataMap;
|
||||||
|
|
||||||
|
_TemplateExtractVisitor(this._metadataMap);
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
viewDef = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// These correspond to the annotations themselves.
|
||||||
|
@override
|
||||||
|
Object visitInstanceCreationExpression(InstanceCreationExpression node) {
|
||||||
|
if ('${node.constructorName.type}' == 'View') {
|
||||||
|
viewDef = new ViewDefinition(directives: <DirectiveMetadata>[]);
|
||||||
|
node.visitChildren(this);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// These correspond to the annotation parameters.
|
||||||
|
@override
|
||||||
|
Object visitNamedExpression(NamedExpression node) {
|
||||||
|
// TODO(kegluneq): Remove this limitation.
|
||||||
|
if (node.name is! Label || node.name.label is! SimpleIdentifier) {
|
||||||
|
logger.error(
|
||||||
|
'Angular 2 currently only supports simple identifiers in directives.'
|
||||||
|
' Source: ${node}');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var keyString = '${node.name.label}';
|
||||||
|
if (keyString == 'directives') {
|
||||||
|
_readDirectives(node.expression);
|
||||||
|
}
|
||||||
|
if (keyString == 'template' || keyString == 'templateUrl') {
|
||||||
|
if (node.expression is! SimpleStringLiteral) {
|
||||||
|
logger.error(
|
||||||
|
'Angular 2 currently only supports string literals in directives.'
|
||||||
|
' Source: ${node}');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var valueString = stringLiteralToString(node.expression);
|
||||||
|
if (keyString == 'templateUrl') {
|
||||||
|
if (viewDef.absUrl != null) {
|
||||||
|
logger.error(
|
||||||
|
'Found multiple values for "templateUrl". Source: ${node}');
|
||||||
|
}
|
||||||
|
viewDef.absUrl = valueString;
|
||||||
|
} else {
|
||||||
|
// keyString == 'template'
|
||||||
|
if (viewDef.template != null) {
|
||||||
|
logger.error('Found multiple values for "template". Source: ${node}');
|
||||||
|
}
|
||||||
|
viewDef.template = valueString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _readDirectives(Expression node) {
|
||||||
|
if (node is! ListLiteral) {
|
||||||
|
logger.error(
|
||||||
|
'Angular 2 currently only supports list literals as values for'
|
||||||
|
' "directives". Source: $node');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var directiveList = (node as ListLiteral);
|
||||||
|
for (var node in directiveList.elements) {
|
||||||
|
if (node is! SimpleIdentifier && node is! PrefixedIdentifier) {
|
||||||
|
logger.error(
|
||||||
|
'Angular 2 currently only supports simple and prefixed identifiers '
|
||||||
|
'as values for "directives". Source: $node');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var name = '$node';
|
||||||
|
if (_metadataMap.containsKey(name)) {
|
||||||
|
viewDef.directives.add(_metadataMap[name]);
|
||||||
|
} else {
|
||||||
|
logger.warning('Could not find Directive entry for $name');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -57,6 +57,8 @@ void allTests() {
|
||||||
outputs: {
|
outputs: {
|
||||||
'a|web/bar.ng_deps.dart':
|
'a|web/bar.ng_deps.dart':
|
||||||
'simple_annotation_files/expected/bar.ng_deps.dart',
|
'simple_annotation_files/expected/bar.ng_deps.dart',
|
||||||
|
'a|web/bar.ng_meta.json':
|
||||||
|
'simple_annotation_files/expected/bar.ng_meta.json',
|
||||||
'a|web/index.ng_deps.dart':
|
'a|web/index.ng_deps.dart':
|
||||||
'simple_annotation_files/expected/index.ng_deps.dart'
|
'simple_annotation_files/expected/index.ng_deps.dart'
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
{"MyComponent":{"id":"MyComponent","selector":"[soup]","compileChildren":true,"hostListeners":{},"hostProperties":{},"properties":{},"readAttributes":[],"type":1,"version":1}}
|
|
@ -58,6 +58,32 @@ void allTests() {
|
||||||
var output = await processTemplates(reader, new AssetId('a', inputPath));
|
var output = await processTemplates(reader, new AssetId('a', inputPath));
|
||||||
_formatThenExpectEquals(output, expected);
|
_formatThenExpectEquals(output, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should parse `View` directives with a single dependency.', () async {
|
||||||
|
var inputPath = 'template_compiler/one_directive_files/hello.ng_deps.dart';
|
||||||
|
var expected = readFile(
|
||||||
|
'template_compiler/one_directive_files/expected/hello.ng_deps.dart');
|
||||||
|
|
||||||
|
var output = await processTemplates(reader, new AssetId('a', inputPath));
|
||||||
|
_formatThenExpectEquals(output, expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse `View` directives with a single prefixed dependency.',
|
||||||
|
() async {
|
||||||
|
var inputPath = 'template_compiler/with_prefix_files/hello.ng_deps.dart';
|
||||||
|
var expected = readFile(
|
||||||
|
'template_compiler/with_prefix_files/expected/hello.ng_deps.dart');
|
||||||
|
|
||||||
|
var output = await processTemplates(reader, new AssetId('a', inputPath));
|
||||||
|
_formatThenExpectEquals(output, expected);
|
||||||
|
|
||||||
|
inputPath = 'template_compiler/with_prefix_files/goodbye.ng_deps.dart';
|
||||||
|
expected = readFile(
|
||||||
|
'template_compiler/with_prefix_files/expected/goodbye.ng_deps.dart');
|
||||||
|
|
||||||
|
output = await processTemplates(reader, new AssetId('a', inputPath));
|
||||||
|
_formatThenExpectEquals(output, expected);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _formatThenExpectEquals(String actual, String expected) {
|
void _formatThenExpectEquals(String actual, String expected) {
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"HelloCmp":
|
||||||
|
{
|
||||||
|
"id":"HelloCmp",
|
||||||
|
"selector":"hello-app",
|
||||||
|
"compileChildren":true,
|
||||||
|
"hostListeners":{},
|
||||||
|
"hostProperties":{},
|
||||||
|
"properties":{},
|
||||||
|
"readAttributes":[],
|
||||||
|
"type":1,
|
||||||
|
"version":1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"HelloCmp":
|
||||||
|
{
|
||||||
|
"id":"HelloCmp",
|
||||||
|
"selector":"hello-app",
|
||||||
|
"compileChildren":true,
|
||||||
|
"hostListeners":{},
|
||||||
|
"hostProperties":{},
|
||||||
|
"properties":{},
|
||||||
|
"readAttributes":[],
|
||||||
|
"type":1,
|
||||||
|
"version":1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"HelloCmp":
|
||||||
|
{
|
||||||
|
"id":"HelloCmp",
|
||||||
|
"selector":"hello-app",
|
||||||
|
"compileChildren":true,
|
||||||
|
"hostListeners":{},
|
||||||
|
"hostProperties":{},
|
||||||
|
"properties":{},
|
||||||
|
"readAttributes":[],
|
||||||
|
"type":1,
|
||||||
|
"version":1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
library examples.hello_world.index_common_dart.ng_deps.dart;
|
||||||
|
|
||||||
|
import 'hello.dart';
|
||||||
|
import 'package:angular2/angular2.dart'
|
||||||
|
show bootstrap, Component, Directive, View, NgElement;
|
||||||
|
|
||||||
|
var _visited = false;
|
||||||
|
void initReflector(reflector) {
|
||||||
|
if (_visited) return;
|
||||||
|
_visited = true;
|
||||||
|
reflector
|
||||||
|
..registerType(HelloCmp, {
|
||||||
|
'factory': () => new HelloCmp(),
|
||||||
|
'parameters': const [const []],
|
||||||
|
'annotations': const [
|
||||||
|
const Component(selector: 'hello-app'),
|
||||||
|
const View(template: 'goodbye-app', directives: const [GoodbyeCmp])
|
||||||
|
]
|
||||||
|
})
|
||||||
|
..registerType(GoodbyeCmp, {
|
||||||
|
'factory': () => new GoodbyeCmp(),
|
||||||
|
'parameters': const [const []],
|
||||||
|
'annotations': const [
|
||||||
|
const Component(selector: 'goodbye-app'),
|
||||||
|
const View(template: 'Goodbye')
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
library examples.hello_world.index_common_dart.ng_deps.dart;
|
||||||
|
|
||||||
|
import 'hello.dart';
|
||||||
|
import 'package:angular2/angular2.dart'
|
||||||
|
show bootstrap, Component, Directive, View, NgElement;
|
||||||
|
|
||||||
|
var _visited = false;
|
||||||
|
void initReflector(reflector) {
|
||||||
|
if (_visited) return;
|
||||||
|
_visited = true;
|
||||||
|
reflector
|
||||||
|
..registerType(HelloCmp, {
|
||||||
|
'factory': () => new HelloCmp(),
|
||||||
|
'parameters': const [const []],
|
||||||
|
'annotations': const [
|
||||||
|
const Component(selector: 'hello-app'),
|
||||||
|
const View(template: 'goodbye-app', directives: const [GoodbyeCmp])
|
||||||
|
]
|
||||||
|
})
|
||||||
|
..registerType(GoodbyeCmp, {
|
||||||
|
'factory': () => new GoodbyeCmp(),
|
||||||
|
'parameters': const [const []],
|
||||||
|
'annotations': const [
|
||||||
|
const Component(selector: 'goodbye-app'),
|
||||||
|
const View(template: 'Goodbye')
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"HelloCmp":
|
||||||
|
{
|
||||||
|
"id":"HelloCmp",
|
||||||
|
"selector":"hello-app",
|
||||||
|
"compileChildren":true,
|
||||||
|
"hostListeners":{},
|
||||||
|
"hostProperties":{},
|
||||||
|
"properties":{},
|
||||||
|
"readAttributes":[],
|
||||||
|
"type":1,
|
||||||
|
"version":1
|
||||||
|
},
|
||||||
|
"GoodbyeCmp":{
|
||||||
|
"id":"GoodbyeCmp",
|
||||||
|
"selector":"goodbye-app",
|
||||||
|
"compileChildren":true,
|
||||||
|
"hostListeners":{},
|
||||||
|
"hostProperties":{},
|
||||||
|
"properties":{},
|
||||||
|
"readAttributes":[],
|
||||||
|
"type":1,
|
||||||
|
"version":1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"HelloCmp":
|
||||||
|
{
|
||||||
|
"id":"HelloCmp",
|
||||||
|
"selector":"hello-app",
|
||||||
|
"compileChildren":true,
|
||||||
|
"hostListeners":{},
|
||||||
|
"hostProperties":{},
|
||||||
|
"properties":{},
|
||||||
|
"readAttributes":[],
|
||||||
|
"type":1,
|
||||||
|
"version":1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"HelloCmp":
|
||||||
|
{
|
||||||
|
"id":"HelloCmp",
|
||||||
|
"selector":"hello-app",
|
||||||
|
"compileChildren":true,
|
||||||
|
"hostListeners":{},
|
||||||
|
"hostProperties":{},
|
||||||
|
"properties":{},
|
||||||
|
"readAttributes":[],
|
||||||
|
"type":1,
|
||||||
|
"version":1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
library examples.hello_world.index_common_dart.ng_deps.dart;
|
||||||
|
|
||||||
|
import 'goodbye.dart';
|
||||||
|
import 'package:angular2/angular2.dart'
|
||||||
|
show bootstrap, Component, Directive, View, NgElement;
|
||||||
|
|
||||||
|
var _visited = false;
|
||||||
|
void initReflector(reflector) {
|
||||||
|
if (_visited) return;
|
||||||
|
_visited = true;
|
||||||
|
reflector
|
||||||
|
..registerType(GoodbyeCmp, {
|
||||||
|
'factory': () => new GoodbyeCmp(),
|
||||||
|
'parameters': const [const []],
|
||||||
|
'annotations': const [
|
||||||
|
const Component(selector: 'goodbye-app'),
|
||||||
|
const View(template: 'Goodbye {{name}}')
|
||||||
|
]
|
||||||
|
})
|
||||||
|
..registerGetters({'name': (o) => o.name})
|
||||||
|
..registerSetters({'name': (o, v) => o.name = v});
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
library examples.hello_world.index_common_dart.ng_deps.dart;
|
||||||
|
|
||||||
|
import 'hello.dart';
|
||||||
|
import 'goodbye.dart' as prefix;
|
||||||
|
import 'goodbye.ng_deps.dart' as i0;
|
||||||
|
import 'package:angular2/angular2.dart'
|
||||||
|
show bootstrap, Component, Directive, View, NgElement;
|
||||||
|
|
||||||
|
var _visited = false;
|
||||||
|
void initReflector(reflector) {
|
||||||
|
if (_visited) return;
|
||||||
|
_visited = true;
|
||||||
|
reflector
|
||||||
|
..registerType(HelloCmp, {
|
||||||
|
'factory': () => new HelloCmp(),
|
||||||
|
'parameters': const [const []],
|
||||||
|
'annotations': const [
|
||||||
|
const Component(selector: 'hello-app'),
|
||||||
|
const View(
|
||||||
|
template: 'goodbye-app', directives: const [prefix.GoodbyeCmp])
|
||||||
|
]
|
||||||
|
});
|
||||||
|
i0.initReflector(reflector);
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
library examples.hello_world.index_common_dart.ng_deps.dart;
|
||||||
|
|
||||||
|
import 'goodbye.dart';
|
||||||
|
import 'package:angular2/angular2.dart'
|
||||||
|
show bootstrap, Component, Directive, View, NgElement;
|
||||||
|
|
||||||
|
var _visited = false;
|
||||||
|
void initReflector(reflector) {
|
||||||
|
if (_visited) return;
|
||||||
|
_visited = true;
|
||||||
|
reflector
|
||||||
|
..registerType(GoodbyeCmp, {
|
||||||
|
'factory': () => new GoodbyeCmp(),
|
||||||
|
'parameters': const [const []],
|
||||||
|
'annotations': const [
|
||||||
|
const Component(selector: 'goodbye-app'),
|
||||||
|
const View(template: 'Goodbye {{name}}')
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"GoodbyeCmp":{
|
||||||
|
"id":"GoodbyeCmp",
|
||||||
|
"selector":"goodbye-app",
|
||||||
|
"compileChildren":true,
|
||||||
|
"hostListeners":{},
|
||||||
|
"hostProperties":{},
|
||||||
|
"properties":{},
|
||||||
|
"readAttributes":[],
|
||||||
|
"type":1,
|
||||||
|
"version":1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
library examples.hello_world.index_common_dart.ng_deps.dart;
|
||||||
|
|
||||||
|
import 'hello.dart';
|
||||||
|
import 'goodbye.dart' as prefix;
|
||||||
|
import 'goodbye.ng_deps.dart' as i0;
|
||||||
|
import 'package:angular2/angular2.dart'
|
||||||
|
show bootstrap, Component, Directive, View, NgElement;
|
||||||
|
|
||||||
|
var _visited = false;
|
||||||
|
void initReflector(reflector) {
|
||||||
|
if (_visited) return;
|
||||||
|
_visited = true;
|
||||||
|
reflector
|
||||||
|
..registerType(HelloCmp, {
|
||||||
|
'factory': () => new HelloCmp(),
|
||||||
|
'parameters': const [const []],
|
||||||
|
'annotations': const [
|
||||||
|
const Component(selector: 'hello-app'),
|
||||||
|
const View(
|
||||||
|
template: 'goodbye-app', directives: const [prefix.GoodbyeCmp])
|
||||||
|
]
|
||||||
|
});
|
||||||
|
i0.initReflector(reflector);
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"HelloCmp":
|
||||||
|
{
|
||||||
|
"id":"HelloCmp",
|
||||||
|
"selector":"hello-app",
|
||||||
|
"compileChildren":true,
|
||||||
|
"hostListeners":{},
|
||||||
|
"hostProperties":{},
|
||||||
|
"properties":{},
|
||||||
|
"readAttributes":[],
|
||||||
|
"type":1,
|
||||||
|
"version":1
|
||||||
|
}
|
||||||
|
}
|
|
@ -46,7 +46,7 @@ function getSourceTree() {
|
||||||
var tsInputTree = modulesFunnel(['**/*.js', '**/*.ts', '**/*.dart'], ['rtts_assert/**/*']);
|
var tsInputTree = modulesFunnel(['**/*.js', '**/*.ts', '**/*.dart'], ['rtts_assert/**/*']);
|
||||||
var transpiled = ts2dart.transpile(tsInputTree);
|
var transpiled = ts2dart.transpile(tsInputTree);
|
||||||
// Native sources, dart only examples, etc.
|
// Native sources, dart only examples, etc.
|
||||||
var dartSrcs = modulesFunnel(['**/*.dart']);
|
var dartSrcs = modulesFunnel(['**/*.dart', '**/*.ng_meta.json']);
|
||||||
return mergeTrees([transpiled, dartSrcs]);
|
return mergeTrees([transpiled, dartSrcs]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue