refactor(dart/transform): Fold bind_generator into other phases
Update `DirectiveProcessor` and `TemplateCompiler` to generate the getters, setters, and methods currently generated in `BindGenerator`. Update `DirectiveMetadataLinker` to output `.ng_meta.json` files instead of `.ng_deps.dart` files, avoiding full codegen until the last phase. This allows us to dedupe codegen logic and remove an additional phase from the transformer.
This commit is contained in:
parent
d68955ac6d
commit
a18358d484
|
@ -180,14 +180,17 @@ class _PropertyMetadataVisitor
|
||||||
List<PropertyMetadataModel> visitFieldDeclaration(FieldDeclaration node) {
|
List<PropertyMetadataModel> visitFieldDeclaration(FieldDeclaration node) {
|
||||||
var retVal = null;
|
var retVal = null;
|
||||||
for (var variable in node.fields.variables) {
|
for (var variable in node.fields.variables) {
|
||||||
var propModel = new PropertyMetadataModel()..name = '${variable.name}';
|
var propModel = null;
|
||||||
for (var meta in node.metadata) {
|
for (var meta in node.metadata) {
|
||||||
var annotationModel = meta.accept(_annotationVisitor);
|
var annotationModel = meta.accept(_annotationVisitor);
|
||||||
if (annotationModel != null) {
|
if (annotationModel != null) {
|
||||||
|
if (propModel == null) {
|
||||||
|
propModel = new PropertyMetadataModel()..name = '${variable.name}';
|
||||||
|
}
|
||||||
propModel.annotations.add(annotationModel);
|
propModel.annotations.add(annotationModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (propModel.annotations.isNotEmpty) {
|
if (propModel != null && propModel.annotations.isNotEmpty) {
|
||||||
if (retVal == null) {
|
if (retVal == null) {
|
||||||
retVal = <PropertyMetadataModel>[];
|
retVal = <PropertyMetadataModel>[];
|
||||||
}
|
}
|
||||||
|
@ -200,14 +203,17 @@ class _PropertyMetadataVisitor
|
||||||
@override
|
@override
|
||||||
List<PropertyMetadataModel> visitMethodDeclaration(MethodDeclaration node) {
|
List<PropertyMetadataModel> visitMethodDeclaration(MethodDeclaration node) {
|
||||||
if (node.isGetter || node.isSetter) {
|
if (node.isGetter || node.isSetter) {
|
||||||
var propModel = new PropertyMetadataModel()..name = '${node.name}';
|
var propModel = null;
|
||||||
for (var meta in node.metadata) {
|
for (var meta in node.metadata) {
|
||||||
var annotationModel = meta.accept(_annotationVisitor);
|
var annotationModel = meta.accept(_annotationVisitor);
|
||||||
if (annotationModel != null) {
|
if (annotationModel != null) {
|
||||||
|
if (propModel == null) {
|
||||||
|
propModel = new PropertyMetadataModel()..name = '${node.name}';
|
||||||
|
}
|
||||||
propModel.annotations.add(annotationModel);
|
propModel.annotations.add(annotationModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (propModel.annotations.isNotEmpty) {
|
if (propModel != null && propModel.annotations.isNotEmpty) {
|
||||||
return <PropertyMetadataModel>[propModel];
|
return <PropertyMetadataModel>[propModel];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,11 +101,9 @@ class NgMeta {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serialized representation of this instance.
|
/// Serialized representation of this instance.
|
||||||
Map toJson({bool withNgDeps: true}) {
|
Map toJson() {
|
||||||
var result = {};
|
var result = {};
|
||||||
if (withNgDeps) {
|
result[_NG_DEPS_KEY] = isNgDepsEmpty ? null : ngDeps.writeToJsonMap();
|
||||||
result[_NG_DEPS_KEY] = isNgDepsEmpty ? null : ngDeps.writeToJsonMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
types.forEach((k, v) {
|
types.forEach((k, v) {
|
||||||
result[k] = {_KIND_KEY: _TYPE_VALUE, _VALUE_KEY: v.toJson()};
|
result[k] = {_KIND_KEY: _TYPE_VALUE, _VALUE_KEY: v.toJson()};
|
||||||
|
|
|
@ -4,8 +4,6 @@ import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:angular2/src/transform/common/asset_reader.dart';
|
import 'package:angular2/src/transform/common/asset_reader.dart';
|
||||||
import 'package:angular2/src/transform/common/code/ng_deps_code.dart';
|
|
||||||
import 'package:angular2/src/transform/common/formatter.dart';
|
|
||||||
import 'package:angular2/src/transform/common/logging.dart' as log;
|
import 'package:angular2/src/transform/common/logging.dart' as log;
|
||||||
import 'package:angular2/src/transform/common/names.dart';
|
import 'package:angular2/src/transform/common/names.dart';
|
||||||
import 'package:barback/barback.dart';
|
import 'package:barback/barback.dart';
|
||||||
|
@ -34,34 +32,14 @@ class DirectiveMetadataLinker extends Transformer {
|
||||||
return linkDirectiveMetadata(
|
return linkDirectiveMetadata(
|
||||||
new AssetReader.fromTransform(transform), primaryId).then((ngMeta) {
|
new AssetReader.fromTransform(transform), primaryId).then((ngMeta) {
|
||||||
if (ngMeta != null) {
|
if (ngMeta != null) {
|
||||||
if (!ngMeta.types.isEmpty || !ngMeta.aliases.isEmpty) {
|
if (!ngMeta.isEmpty) {
|
||||||
transform.addOutput(new Asset.fromString(
|
transform.addOutput(new Asset.fromString(
|
||||||
primaryId, _encoder.convert(ngMeta.toJson(withNgDeps: false))));
|
primaryId, _encoder.convert(ngMeta.toJson())));
|
||||||
} else {
|
} else {
|
||||||
// Not outputting an asset could confuse barback.
|
// Not outputting an asset could confuse barback.
|
||||||
transform.addOutput(new Asset.fromString(primaryId, ''));
|
transform.addOutput(new Asset.fromString(primaryId, ''));
|
||||||
}
|
}
|
||||||
|
} });
|
||||||
var depsAssetId = _depsAssetId(primaryId);
|
|
||||||
if (!ngMeta.isNgDepsEmpty) {
|
|
||||||
var buf = new StringBuffer();
|
|
||||||
var writer = new NgDepsWriter(buf);
|
|
||||||
writer.writeNgDepsModel(ngMeta.ngDeps);
|
|
||||||
var formattedCode =
|
|
||||||
formatter.format(buf.toString(), uri: depsAssetId.path);
|
|
||||||
transform
|
|
||||||
.addOutput(new Asset.fromString(depsAssetId, formattedCode));
|
|
||||||
} else {
|
|
||||||
transform.addOutput(
|
|
||||||
new Asset.fromString(depsAssetId, _emptyNgDepsContents));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AssetId _depsAssetId(AssetId primaryId) =>
|
|
||||||
new AssetId(primaryId.package, toDepsExtension(primaryId.path));
|
|
||||||
|
|
||||||
const _emptyNgDepsContents = 'initReflector() {}';
|
|
||||||
|
|
|
@ -3,54 +3,91 @@ library angular2.transform.template_compiler.compile_data_creator;
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:analyzer/analyzer.dart';
|
|
||||||
import 'package:angular2/src/core/compiler/directive_metadata.dart';
|
import 'package:angular2/src/core/compiler/directive_metadata.dart';
|
||||||
import 'package:angular2/src/core/compiler/template_compiler.dart';
|
import 'package:angular2/src/core/compiler/template_compiler.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/model/import_export_model.pb.dart';
|
||||||
|
import 'package:angular2/src/transform/common/model/ng_deps_model.pb.dart';
|
||||||
|
import 'package:angular2/src/transform/common/model/reflection_info_model.pb.dart';
|
||||||
import 'package:angular2/src/transform/common/names.dart';
|
import 'package:angular2/src/transform/common/names.dart';
|
||||||
import 'package:angular2/src/transform/common/ng_deps.dart';
|
|
||||||
import 'package:angular2/src/transform/common/ng_meta.dart';
|
import 'package:angular2/src/transform/common/ng_meta.dart';
|
||||||
|
import 'package:angular2/src/transform/common/url_resolver.dart';
|
||||||
import 'package:barback/barback.dart';
|
import 'package:barback/barback.dart';
|
||||||
import 'package:code_transformers/assets.dart';
|
|
||||||
|
|
||||||
/// Creates [NormalizedComponentWithViewDirectives] objects for all `View`
|
/// Creates [NormalizedComponentWithViewDirectives] objects for all `View`
|
||||||
/// `Directive`s defined in `entryPoint`.
|
/// `Directive`s defined in `assetId`.
|
||||||
///
|
///
|
||||||
/// The returned value wraps the [NgDeps] at `entryPoint` as well as these
|
/// The returned value wraps the [NgDepsModel] at `assetId` as well as these
|
||||||
/// created objects.
|
/// created objects.
|
||||||
Future<CompileDataResults> createCompileData(
|
Future<CompileDataResults> createCompileData(
|
||||||
AssetReader reader, AssetId assetId) async {
|
AssetReader reader, AssetId assetId) async {
|
||||||
return logElapsedAsync(() {
|
return logElapsedAsync(() async {
|
||||||
return new _CompileDataCreator(reader, assetId).createCompileData();
|
return (await _CompileDataCreator.create(reader, assetId))
|
||||||
|
.createCompileData();
|
||||||
}, operationName: 'createCompileData', assetId: assetId);
|
}, operationName: 'createCompileData', assetId: assetId);
|
||||||
}
|
}
|
||||||
|
|
||||||
class CompileDataResults {
|
class CompileDataResults {
|
||||||
final NgDeps ngDeps;
|
final NgMeta ngMeta;
|
||||||
final Map<RegisteredType,
|
final Map<ReflectionInfoModel,
|
||||||
NormalizedComponentWithViewDirectives> viewDefinitions;
|
NormalizedComponentWithViewDirectives> viewDefinitions;
|
||||||
final List<CompileDirectiveMetadata> directiveMetadatas;
|
final List<CompileDirectiveMetadata> directiveMetadatas;
|
||||||
|
|
||||||
CompileDataResults._(
|
CompileDataResults._(
|
||||||
this.ngDeps, this.viewDefinitions, this.directiveMetadatas);
|
this.ngMeta, this.viewDefinitions, this.directiveMetadatas);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(kegluenq): Improve this test.
|
class _PrefixedDirectiveName {
|
||||||
bool _isViewAnnotation(InstanceCreationExpression node) {
|
final String prefix;
|
||||||
var constructorName = node.constructorName.type.name;
|
final String directiveName;
|
||||||
if (constructorName is PrefixedIdentifier) {
|
|
||||||
constructorName = constructorName.identifier;
|
_PrefixedDirectiveName(String prefix, this.directiveName)
|
||||||
|
: this.prefix = prefix == null ? '' : prefix;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
if (prefix.isEmpty) {
|
||||||
|
return directiveName;
|
||||||
|
} else {
|
||||||
|
return '${prefix}.${directiveName}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return constructorName.name == 'View';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _isComponentAnnotation(InstanceCreationExpression node) {
|
String _directivesValue(ReflectionInfoModel model, bool test(AnnotationModel)) {
|
||||||
var constructorName = node.constructorName.type.name;
|
final viewAnnotation = model.annotations.firstWhere(test, orElse: () => null);
|
||||||
if (constructorName is PrefixedIdentifier) {
|
if (viewAnnotation == null) return null;
|
||||||
constructorName = constructorName.identifier;
|
final directivesParam = viewAnnotation.namedParameters
|
||||||
|
.firstWhere((p) => p.name == 'directives', orElse: () => null);
|
||||||
|
return directivesParam != null ? directivesParam.value : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(kegluneq): Parse this value when creating [NgDepsModel]?
|
||||||
|
/// Find the `directives` parameter on the @View annotation and make sure that
|
||||||
|
/// all necessary [CompileDirectiveMetadata] objects are available.
|
||||||
|
Iterable<_PrefixedDirectiveName> _getDirectiveDeps(ReflectionInfoModel model) {
|
||||||
|
var directives = _directivesValue(model, (m) => m.isView);
|
||||||
|
if (directives == null) {
|
||||||
|
directives = _directivesValue(model, (m) => m.isComponent);
|
||||||
}
|
}
|
||||||
return constructorName.name == 'Component';
|
if (directives == null) return const [];
|
||||||
|
directives = directives.trim();
|
||||||
|
for (var toTrim in ['const [', '[']) {
|
||||||
|
if (directives.startsWith(toTrim) && directives.endsWith(']')) {
|
||||||
|
directives =
|
||||||
|
directives.substring(toTrim.length, directives.length - 1).trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (directives.length == 0) return const [];
|
||||||
|
return directives.split(',').map((p) {
|
||||||
|
var parts = p.trim().split('.');
|
||||||
|
if (parts.length == 1) {
|
||||||
|
return new _PrefixedDirectiveName(null, parts[0]);
|
||||||
|
} else {
|
||||||
|
return new _PrefixedDirectiveName(parts[0], parts[1]);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates [ViewDefinition] objects for all `View` `Directive`s defined in
|
/// Creates [ViewDefinition] objects for all `View` `Directive`s defined in
|
||||||
|
@ -58,37 +95,65 @@ bool _isComponentAnnotation(InstanceCreationExpression node) {
|
||||||
class _CompileDataCreator {
|
class _CompileDataCreator {
|
||||||
final AssetReader reader;
|
final AssetReader reader;
|
||||||
final AssetId entryPoint;
|
final AssetId entryPoint;
|
||||||
final Future<NgDeps> ngDepsFuture;
|
final NgMeta ngMeta;
|
||||||
final List<CompileDirectiveMetadata> directiveMetadatas = [];
|
final directiveMetadatas = <CompileDirectiveMetadata>[];
|
||||||
|
|
||||||
_CompileDataCreator(AssetReader reader, AssetId entryPoint)
|
_CompileDataCreator(this.reader, this.entryPoint, this.ngMeta);
|
||||||
: this.reader = reader,
|
|
||||||
this.entryPoint = entryPoint,
|
static Future<_CompileDataCreator> create(
|
||||||
ngDepsFuture = NgDeps.parse(reader, entryPoint);
|
AssetReader reader, AssetId assetId) async {
|
||||||
|
if (!(await reader.hasInput(assetId))) return null;
|
||||||
|
final json = await reader.readAsString(assetId);
|
||||||
|
if (json == null || json.isEmpty) return null;
|
||||||
|
|
||||||
|
final ngMeta = new NgMeta.fromJson(JSON.decode(json));
|
||||||
|
return new _CompileDataCreator(reader, assetId, ngMeta);
|
||||||
|
}
|
||||||
|
|
||||||
|
NgDepsModel get ngDeps => ngMeta.ngDeps;
|
||||||
|
|
||||||
Future<CompileDataResults> createCompileData() async {
|
Future<CompileDataResults> createCompileData() async {
|
||||||
var ngDeps = await ngDepsFuture;
|
if (ngDeps == null || ngDeps.reflectables == null) {
|
||||||
|
return new CompileDataResults._(ngMeta, const {}, directiveMetadatas);
|
||||||
|
}
|
||||||
|
|
||||||
var retVal = <RegisteredType, NormalizedComponentWithViewDirectives>{};
|
final compileData =
|
||||||
var visitor = new _DirectiveDependenciesVisitor(await _extractNgMeta());
|
<ReflectionInfoModel, NormalizedComponentWithViewDirectives>{};
|
||||||
ngDeps.registeredTypes.forEach((rType) {
|
final ngMetaMap = await _extractNgMeta();
|
||||||
visitor.reset();
|
|
||||||
rType.annotations.accept(visitor);
|
|
||||||
if (visitor.compileData != null) {
|
|
||||||
// Note: we use '' because the current file maps to the default prefix.
|
|
||||||
var ngMeta = visitor._metadataMap[''];
|
|
||||||
var typeName = '${rType.typeName}';
|
|
||||||
|
|
||||||
if (ngMeta.types.containsKey(typeName)) {
|
for (var reflectable in ngDeps.reflectables) {
|
||||||
visitor.compileData.component = ngMeta.types[typeName];
|
if (ngMeta.types.containsKey(reflectable.name)) {
|
||||||
} else {
|
final compileDirectiveMetadata = ngMeta.types[reflectable.name];
|
||||||
logger.warning('Missing component "$typeName" in metadata map',
|
if (compileDirectiveMetadata.template != null) {
|
||||||
asset: entryPoint);
|
final compileDatum = new NormalizedComponentWithViewDirectives(
|
||||||
|
compileDirectiveMetadata, <CompileDirectiveMetadata>[]);
|
||||||
|
for (var dep in _getDirectiveDeps(reflectable)) {
|
||||||
|
if (!ngMetaMap.containsKey(dep.prefix)) {
|
||||||
|
logger.warning(
|
||||||
|
'Missing prefix "${dep.prefix}" '
|
||||||
|
'needed by "${dep}" from metadata map',
|
||||||
|
asset: entryPoint);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final depNgMeta = ngMetaMap[dep.prefix];
|
||||||
|
|
||||||
|
if (depNgMeta.types.containsKey(dep.directiveName)) {
|
||||||
|
compileDatum.directives.add(depNgMeta.types[dep.directiveName]);
|
||||||
|
} else if (depNgMeta.aliases.containsKey(dep.directiveName)) {
|
||||||
|
compileDatum.directives
|
||||||
|
.addAll(depNgMeta.flatten(dep.directiveName));
|
||||||
|
} else {
|
||||||
|
logger.warning('Could not find Directive entry for $dep. '
|
||||||
|
'Please be aware that Dart transformers have limited support for '
|
||||||
|
'reusable, pre-defined lists of Directives (aka '
|
||||||
|
'"directive aliases"). See https://goo.gl/d8XPt0 for details.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compileData[reflectable] = compileDatum;
|
||||||
}
|
}
|
||||||
retVal[rType] = visitor.compileData;
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
return new CompileDataResults._(ngDeps, retVal, directiveMetadatas);
|
return new CompileDataResults._(ngMeta, compileData, directiveMetadatas);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a map from [AssetId] to import prefix for `.dart` libraries
|
/// Creates a map from [AssetId] to import prefix for `.dart` libraries
|
||||||
|
@ -96,27 +161,27 @@ class _CompileDataCreator {
|
||||||
/// Unprefixed imports have `null` as their value. `entryPoint` is included
|
/// Unprefixed imports have `null` as their value. `entryPoint` is included
|
||||||
/// in the map with no prefix.
|
/// in the map with no prefix.
|
||||||
Future<Map<AssetId, String>> _createImportAssetToPrefixMap() async {
|
Future<Map<AssetId, String>> _createImportAssetToPrefixMap() async {
|
||||||
var ngDeps = await ngDepsFuture;
|
final importAssetToPrefix = <AssetId, String>{entryPoint: null};
|
||||||
|
if (ngDeps == null || ngDeps.imports == null || ngDeps.imports.isEmpty) {
|
||||||
|
return importAssetToPrefix;
|
||||||
|
}
|
||||||
|
final baseUri = toAssetUri(entryPoint);
|
||||||
|
final resolver = const TransformerUrlResolver();
|
||||||
|
|
||||||
var importAssetToPrefix = <AssetId, String>{entryPoint: null};
|
for (ImportModel model in ngMeta.ngDeps.imports) {
|
||||||
|
if (model.uri.endsWith('.dart') && !model.isNgDeps) {
|
||||||
for (ImportDirective node in ngDeps.imports) {
|
var prefix =
|
||||||
var uri = stringLiteralToString(node.uri);
|
model.prefix != null && model.prefix.isEmpty ? null : model.prefix;
|
||||||
if (uri.endsWith('.dart') && !uri.endsWith(DEPS_EXTENSION)) {
|
importAssetToPrefix[fromUri(resolver.resolve(baseUri, model.uri))] =
|
||||||
var prefix = node.prefix != null && node.prefix.name != null
|
prefix;
|
||||||
? '${node.prefix.name}'
|
|
||||||
: null;
|
|
||||||
importAssetToPrefix[uriToAssetId(
|
|
||||||
entryPoint, uri, logger, null /* span */,
|
|
||||||
errorOnAbsolute: false)] = prefix;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return importAssetToPrefix;
|
return importAssetToPrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads the `.ng_meta.json` files associated with all of `entryPoint`'s
|
/// Reads the `.ng_meta.json` files associated with all of `entryPoint`'s
|
||||||
/// imports and creates a map `Type` name, prefixed if appropriate to the
|
/// imports and creates a map of prefix (or blank) to the
|
||||||
/// associated [CompileDirectiveMetadata].
|
/// associated [NgMeta] object.
|
||||||
///
|
///
|
||||||
/// For example, if in `entryPoint` we have:
|
/// For example, if in `entryPoint` we have:
|
||||||
///
|
///
|
||||||
|
@ -132,13 +197,13 @@ class _CompileDataCreator {
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// This method will look for `component.ng_meta.json`to contain the
|
/// This method will look for `component.ng_meta.json`to contain the
|
||||||
/// serialized [CompileDirectiveMetadata] for `MyComponent` and any other
|
/// serialized [NgMeta] for `MyComponent` and any other
|
||||||
/// `Directive`s declared in `component.dart`. We use this information to
|
/// `Directive`s declared in `component.dart`. We use this information to
|
||||||
/// build a map:
|
/// build a map:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// {
|
/// {
|
||||||
/// "prefix.MyComponent": [CompileDirectiveMetadata for MyComponent],
|
/// "prefix": [NgMeta with CompileDirectiveMetadata for MyComponent],
|
||||||
/// ...<any other entries>...
|
/// ...<any other entries>...
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -172,98 +237,3 @@ class _CompileDataCreator {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Visitor responsible for processing the `annotations` property of a
|
|
||||||
/// [RegisterType] object, extracting the `directives` dependencies, and adding
|
|
||||||
/// their associated [CompileDirectiveMetadata] to the `directives` of a
|
|
||||||
/// created [NormalizedComponentWithViewDirectives] object.
|
|
||||||
///
|
|
||||||
/// The `component` property of the created
|
|
||||||
/// [NormalizedComponentWithViewDirectives] will be null.
|
|
||||||
///
|
|
||||||
/// If no `View` annotation is found, `compileData` will be null.
|
|
||||||
class _DirectiveDependenciesVisitor extends Object
|
|
||||||
with RecursiveAstVisitor<Object> {
|
|
||||||
NormalizedComponentWithViewDirectives compileData = null;
|
|
||||||
final Map<String, NgMeta> _metadataMap;
|
|
||||||
|
|
||||||
_DirectiveDependenciesVisitor(this._metadataMap);
|
|
||||||
|
|
||||||
void reset() {
|
|
||||||
compileData = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// These correspond to the annotations themselves, which are converted into
|
|
||||||
/// const instance creation expressions so they can be stored in the
|
|
||||||
/// reflector.
|
|
||||||
@override
|
|
||||||
Object visitInstanceCreationExpression(InstanceCreationExpression node) {
|
|
||||||
if (_isViewAnnotation(node) || _isComponentAnnotation(node)) {
|
|
||||||
if (compileData == null) {
|
|
||||||
compileData = new NormalizedComponentWithViewDirectives(
|
|
||||||
null, <CompileDirectiveMetadata>[]);
|
|
||||||
} else {
|
|
||||||
// This is set above, after the visitor is finished. If this value is
|
|
||||||
// non-null it indicates that we forgot to call `reset()`.
|
|
||||||
assert(compileData.component == null);
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
if ('${node.name.label}' == 'directives') {
|
|
||||||
_readDirectives(node.expression);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _readDirectives(Expression node) {
|
|
||||||
// This could happen in a non-View annotation with a `directives`
|
|
||||||
// parameter.
|
|
||||||
if (compileData == null) return;
|
|
||||||
|
|
||||||
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) {
|
|
||||||
var ngMeta;
|
|
||||||
var name;
|
|
||||||
if (node is SimpleIdentifier) {
|
|
||||||
ngMeta = _metadataMap[''];
|
|
||||||
name = node.name;
|
|
||||||
} else if (node is PrefixedIdentifier) {
|
|
||||||
ngMeta = _metadataMap[node.prefix.name];
|
|
||||||
name = node.identifier.name;
|
|
||||||
} else {
|
|
||||||
logger.error(
|
|
||||||
'Angular 2 currently only supports simple and prefixed identifiers '
|
|
||||||
'as values for "directives". Source: $node');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (ngMeta.types.containsKey(name)) {
|
|
||||||
compileData.directives.add(ngMeta.types[name]);
|
|
||||||
} else if (ngMeta.aliases.containsKey(name)) {
|
|
||||||
compileData.directives.addAll(ngMeta.flatten(name));
|
|
||||||
} else {
|
|
||||||
logger.warning('Could not find Directive entry for $node. '
|
|
||||||
'Please be aware that Dart transformers have limited support for '
|
|
||||||
'reusable, pre-defined lists of Directives (aka '
|
|
||||||
'"directive aliases"). See https://goo.gl/d8XPt0 for details.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,22 +2,20 @@ library angular2.transform.template_compiler.generator;
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:analyzer/analyzer.dart';
|
|
||||||
import 'package:angular2/src/core/compiler/source_module.dart';
|
|
||||||
import 'package:angular2/src/core/compiler/template_compiler.dart';
|
|
||||||
import 'package:angular2/src/core/change_detection/interfaces.dart';
|
import 'package:angular2/src/core/change_detection/interfaces.dart';
|
||||||
import 'package:angular2/src/core/facade/lang.dart';
|
import 'package:angular2/src/core/facade/lang.dart';
|
||||||
import 'package:angular2/src/core/reflection/reflection.dart';
|
import 'package:angular2/src/core/reflection/reflection.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/code/source_module.dart';
|
import 'package:angular2/src/transform/common/code/source_module.dart';
|
||||||
import 'package:angular2/src/transform/common/logging.dart';
|
import 'package:angular2/src/transform/common/logging.dart';
|
||||||
|
import 'package:angular2/src/transform/common/model/annotation_model.pb.dart';
|
||||||
|
import 'package:angular2/src/transform/common/model/import_export_model.pb.dart';
|
||||||
|
import 'package:angular2/src/transform/common/model/ng_deps_model.pb.dart';
|
||||||
import 'package:angular2/src/transform/common/names.dart';
|
import 'package:angular2/src/transform/common/names.dart';
|
||||||
import 'package:angular2/src/transform/common/ng_compiler.dart';
|
import 'package:angular2/src/transform/common/ng_compiler.dart';
|
||||||
import 'package:angular2/src/transform/common/ng_deps.dart';
|
|
||||||
import 'package:barback/barback.dart';
|
import 'package:barback/barback.dart';
|
||||||
import 'package:path/path.dart' as path;
|
import 'package:path/path.dart' as path;
|
||||||
|
|
||||||
import 'reflection/codegen.dart' as reg;
|
|
||||||
import 'reflection/processor.dart' as reg;
|
import 'reflection/processor.dart' as reg;
|
||||||
import 'reflection/reflection_capabilities.dart';
|
import 'reflection/reflection_capabilities.dart';
|
||||||
import 'compile_data_creator.dart';
|
import 'compile_data_creator.dart';
|
||||||
|
@ -30,22 +28,24 @@ import 'compile_data_creator.dart';
|
||||||
Future<Outputs> processTemplates(AssetReader reader, AssetId assetId,
|
Future<Outputs> processTemplates(AssetReader reader, AssetId assetId,
|
||||||
{bool reflectPropertiesAsAttributes: false}) async {
|
{bool reflectPropertiesAsAttributes: false}) async {
|
||||||
var viewDefResults = await createCompileData(reader, assetId);
|
var viewDefResults = await createCompileData(reader, assetId);
|
||||||
var codegen = null;
|
|
||||||
if (viewDefResults.directiveMetadatas.isNotEmpty) {
|
if (viewDefResults.directiveMetadatas.isNotEmpty) {
|
||||||
var processor = new reg.Processor();
|
var processor = new reg.Processor();
|
||||||
viewDefResults.directiveMetadatas.forEach(processor.process);
|
viewDefResults.directiveMetadatas.forEach(processor.process);
|
||||||
codegen = new reg.Codegen();
|
viewDefResults.ngMeta.ngDeps.getters
|
||||||
codegen.generate(processor);
|
.addAll(processor.getterNames.map((e) => e.sanitizedName));
|
||||||
|
viewDefResults.ngMeta.ngDeps.setters
|
||||||
|
.addAll(processor.setterNames.map((e) => e.sanitizedName));
|
||||||
|
viewDefResults.ngMeta.ngDeps.methods
|
||||||
|
.addAll(processor.methodNames.map((e) => e.sanitizedName));
|
||||||
}
|
}
|
||||||
var templateCompiler = createTemplateCompiler(reader,
|
var templateCompiler = createTemplateCompiler(reader,
|
||||||
changeDetectionConfig: new ChangeDetectorGenConfig(assertionsEnabled(),
|
changeDetectionConfig: new ChangeDetectorGenConfig(assertionsEnabled(),
|
||||||
assertionsEnabled(), reflectPropertiesAsAttributes, false));
|
assertionsEnabled(), reflectPropertiesAsAttributes, false));
|
||||||
|
|
||||||
var ngDeps = viewDefResults.ngDeps;
|
final compileData =
|
||||||
var compileData =
|
|
||||||
viewDefResults.viewDefinitions.values.toList(growable: false);
|
viewDefResults.viewDefinitions.values.toList(growable: false);
|
||||||
if (compileData.isEmpty) {
|
if (compileData.isEmpty) {
|
||||||
return new Outputs(assetId, ngDeps, codegen, null, null);
|
return new Outputs._(viewDefResults.ngMeta.ngDeps, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
var savedReflectionCapabilities = reflector.reflectionCapabilities;
|
var savedReflectionCapabilities = reflector.reflectionCapabilities;
|
||||||
|
@ -55,86 +55,30 @@ Future<Outputs> processTemplates(AssetReader reader, AssetId assetId,
|
||||||
}, operationName: 'compileTemplatesCodegen', assetId: assetId);
|
}, operationName: 'compileTemplatesCodegen', assetId: assetId);
|
||||||
reflector.reflectionCapabilities = savedReflectionCapabilities;
|
reflector.reflectionCapabilities = savedReflectionCapabilities;
|
||||||
|
|
||||||
return new Outputs(assetId, ngDeps, codegen, viewDefResults.viewDefinitions,
|
if (compiledTemplates != null) {
|
||||||
compiledTemplates);
|
viewDefResults.ngMeta.ngDeps.imports.add(new ImportModel()
|
||||||
|
..uri = toTemplateExtension(path.basename(assetId.path))
|
||||||
|
..prefix = '_templates');
|
||||||
|
for (var reflectable in viewDefResults.viewDefinitions.keys) {
|
||||||
|
reflectable.annotations.add(new AnnotationModel()
|
||||||
|
..name = '_templates.Host${reflectable.name}Template'
|
||||||
|
..isConstObject = true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Outputs._(
|
||||||
|
viewDefResults.ngMeta.ngDeps, writeSourceModule(compiledTemplates));
|
||||||
}
|
}
|
||||||
|
|
||||||
AssetId templatesAssetId(AssetId ngDepsAssetId) =>
|
AssetId ngDepsAssetId(AssetId primaryId) =>
|
||||||
new AssetId(ngDepsAssetId.package, toTemplateExtension(ngDepsAssetId.path));
|
new AssetId(primaryId.package, toDepsExtension(primaryId.path));
|
||||||
|
|
||||||
|
AssetId templatesAssetId(AssetId primaryId) =>
|
||||||
|
new AssetId(primaryId.package, toTemplateExtension(primaryId.path));
|
||||||
|
|
||||||
class Outputs {
|
class Outputs {
|
||||||
final String ngDepsCode;
|
final NgDepsModel ngDeps;
|
||||||
final String templatesCode;
|
final String templatesCode;
|
||||||
|
|
||||||
Outputs._(this.ngDepsCode, this.templatesCode);
|
Outputs._(this.ngDeps, this.templatesCode);
|
||||||
|
|
||||||
factory Outputs(
|
|
||||||
AssetId assetId,
|
|
||||||
NgDeps ngDeps,
|
|
||||||
reg.Codegen accessors,
|
|
||||||
Map<RegisteredType, NormalizedComponentWithViewDirectives> compileDataMap,
|
|
||||||
SourceModule templatesSource) {
|
|
||||||
return new Outputs._(
|
|
||||||
_generateNgDepsCode(assetId, ngDeps, accessors, compileDataMap),
|
|
||||||
writeSourceModule(templatesSource));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Updates the NgDeps code with an additional `CompiledTemplate` annotation
|
|
||||||
// for each Directive we generated one for.
|
|
||||||
//
|
|
||||||
// Also adds an import to the `.template.dart` file that we will generate.
|
|
||||||
static String _generateNgDepsCode(
|
|
||||||
AssetId id,
|
|
||||||
NgDeps ngDeps,
|
|
||||||
reg.Codegen accessors,
|
|
||||||
Map<RegisteredType,
|
|
||||||
NormalizedComponentWithViewDirectives> compileDataMap) {
|
|
||||||
var code = ngDeps.code;
|
|
||||||
if (accessors == null &&
|
|
||||||
(compileDataMap == null || compileDataMap.isEmpty)) return code;
|
|
||||||
|
|
||||||
if (ngDeps.registeredTypes.isEmpty) return code;
|
|
||||||
var beginRegistrationsIdx =
|
|
||||||
ngDeps.registeredTypes.first.registerMethod.offset;
|
|
||||||
var endRegistratationsIdx = ngDeps.registeredTypes.last.registerMethod.end;
|
|
||||||
var importInjectIdx = ngDeps.lib != null ? ngDeps.lib.end : 0;
|
|
||||||
|
|
||||||
// Add everything up to the point where we begin registering classes with
|
|
||||||
// the reflector, injecting an import to the generated template code.
|
|
||||||
var buf;
|
|
||||||
if (compileDataMap != null) {
|
|
||||||
buf = new StringBuffer('${code.substring(0, importInjectIdx)}'
|
|
||||||
'import \'${toTemplateExtension(path.basename(id.path))}\' as _templates;'
|
|
||||||
'${code.substring(importInjectIdx, beginRegistrationsIdx)}');
|
|
||||||
} else {
|
|
||||||
buf = new StringBuffer('${code.substring(0, beginRegistrationsIdx)}');
|
|
||||||
}
|
|
||||||
for (var registeredType in ngDeps.registeredTypes) {
|
|
||||||
if (compileDataMap != null &&
|
|
||||||
compileDataMap.containsKey(registeredType)) {
|
|
||||||
// We generated a template for this type, so add the generated
|
|
||||||
// `CompiledTemplate` value as the final annotation in the list.
|
|
||||||
var annotations = registeredType.annotations as ListLiteral;
|
|
||||||
if (annotations.length == 0) {
|
|
||||||
throw new FormatException('Unexpected format - attempting to codegen '
|
|
||||||
'a class with no Component annotation ${registeredType.typeName}');
|
|
||||||
}
|
|
||||||
buf.write(code.substring(registeredType.registerMethod.offset,
|
|
||||||
annotations.elements.last.end));
|
|
||||||
buf.write(', _templates.Host${registeredType.typeName}Template]');
|
|
||||||
buf.writeln(code.substring(
|
|
||||||
registeredType.annotations.end, registeredType.registerMethod.end));
|
|
||||||
} else {
|
|
||||||
// There is no compiled template for this type, write it out without any
|
|
||||||
// changes.
|
|
||||||
buf.writeln(code.substring(registeredType.registerMethod.offset,
|
|
||||||
registeredType.registerMethod.end));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buf.writeln(accessors.toString());
|
|
||||||
|
|
||||||
// Add everything after the final registration.
|
|
||||||
buf.writeln(code.substring(endRegistratationsIdx));
|
|
||||||
return buf.toString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,82 +0,0 @@
|
||||||
library angular2.transform.template_compiler.reflection.codegen;
|
|
||||||
|
|
||||||
import 'package:angular2/src/transform/common/names.dart';
|
|
||||||
import 'package:angular2/src/transform/common/property_utils.dart' as prop;
|
|
||||||
|
|
||||||
import 'model.dart';
|
|
||||||
|
|
||||||
class Codegen {
|
|
||||||
final StringBuffer _buf = new StringBuffer();
|
|
||||||
|
|
||||||
/// Generates code to register all getters, setters, and methods stored by
|
|
||||||
/// `model`.
|
|
||||||
///
|
|
||||||
/// The code takes the form of zero or more cascaded calls. The receiver of
|
|
||||||
/// these calls is expected to be an Angular 2 reflector object.
|
|
||||||
void generate(CodegenModel model) {
|
|
||||||
if (model != null) {
|
|
||||||
var calls = _generateGetters(_extractNames(model.getterNames));
|
|
||||||
if (calls.isNotEmpty) {
|
|
||||||
_buf.write('..${REGISTER_GETTERS_METHOD_NAME}'
|
|
||||||
'({${calls.join(', ')}})');
|
|
||||||
}
|
|
||||||
calls = _generateSetters(_extractNames(model.setterNames));
|
|
||||||
if (calls.isNotEmpty) {
|
|
||||||
_buf.write('..${REGISTER_SETTERS_METHOD_NAME}'
|
|
||||||
'({${calls.join(', ')}})');
|
|
||||||
}
|
|
||||||
calls = _generateMethods(_extractNames(model.methodNames));
|
|
||||||
if (calls.isNotEmpty) {
|
|
||||||
_buf.write('..${REGISTER_METHODS_METHOD_NAME}'
|
|
||||||
'({${calls.join(', ')}})');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Iterable<String> _extractNames(Iterable<ReflectiveAccessor> accessors) {
|
|
||||||
var names = accessors.map((accessor) => accessor.sanitizedName);
|
|
||||||
var nameList = names.toList();
|
|
||||||
nameList.sort();
|
|
||||||
return nameList;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get isEmpty => _buf.isEmpty;
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() => '$_buf';
|
|
||||||
}
|
|
||||||
|
|
||||||
Iterable<String> _generateGetters(Iterable<String> getterNames) {
|
|
||||||
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''';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Iterable<String> _generateSetters(Iterable<String> setterName) {
|
|
||||||
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 ''';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Iterable<String> _generateMethods(Iterable<String> methodNames) {
|
|
||||||
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) ';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -4,6 +4,7 @@ import 'dart:async';
|
||||||
|
|
||||||
import 'package:angular2/src/core/dom/html_adapter.dart';
|
import 'package:angular2/src/core/dom/html_adapter.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/code/ng_deps_code.dart';
|
||||||
import 'package:angular2/src/transform/common/formatter.dart';
|
import 'package:angular2/src/transform/common/formatter.dart';
|
||||||
import 'package:angular2/src/transform/common/logging.dart' as log;
|
import 'package:angular2/src/transform/common/logging.dart' as log;
|
||||||
import 'package:angular2/src/transform/common/names.dart';
|
import 'package:angular2/src/transform/common/names.dart';
|
||||||
|
@ -24,7 +25,7 @@ class TemplateCompiler extends Transformer {
|
||||||
TemplateCompiler(this.options);
|
TemplateCompiler(this.options);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool isPrimary(AssetId id) => id.path.endsWith(DEPS_EXTENSION);
|
bool isPrimary(AssetId id) => id.path.endsWith(META_EXTENSION);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future apply(Transform transform) async {
|
Future apply(Transform transform) async {
|
||||||
|
@ -34,19 +35,25 @@ class TemplateCompiler extends Transformer {
|
||||||
var reader = new AssetReader.fromTransform(transform);
|
var reader = new AssetReader.fromTransform(transform);
|
||||||
var outputs = await processTemplates(reader, primaryId,
|
var outputs = await processTemplates(reader, primaryId,
|
||||||
reflectPropertiesAsAttributes: options.reflectPropertiesAsAttributes);
|
reflectPropertiesAsAttributes: options.reflectPropertiesAsAttributes);
|
||||||
var ngDepsCode = '';
|
var ngDepsCode = _emptyNgDepsContents;
|
||||||
var templatesCode = '';
|
var templatesCode = '';
|
||||||
if (outputs != null) {
|
if (outputs != null) {
|
||||||
if (outputs.ngDepsCode != null) {
|
if (outputs.ngDeps != null) {
|
||||||
ngDepsCode = formatter.format(outputs.ngDepsCode);
|
final buf = new StringBuffer();
|
||||||
|
final writer = new NgDepsWriter(buf);
|
||||||
|
writer.writeNgDepsModel(outputs.ngDeps);
|
||||||
|
ngDepsCode = formatter.format(buf.toString());
|
||||||
}
|
}
|
||||||
if (outputs.templatesCode != null) {
|
if (outputs.templatesCode != null) {
|
||||||
templatesCode = formatter.format(outputs.templatesCode);
|
templatesCode = formatter.format(outputs.templatesCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
transform.addOutput(new Asset.fromString(primaryId, ngDepsCode));
|
transform.addOutput(
|
||||||
|
new Asset.fromString(ngDepsAssetId(primaryId), ngDepsCode));
|
||||||
transform.addOutput(
|
transform.addOutput(
|
||||||
new Asset.fromString(templatesAssetId(primaryId), templatesCode));
|
new Asset.fromString(templatesAssetId(primaryId), templatesCode));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const _emptyNgDepsContents = 'initReflector() {}\n';
|
||||||
|
|
|
@ -40,8 +40,8 @@ class AngularTransformerGroup extends TransformerGroup {
|
||||||
[
|
[
|
||||||
new TemplateCompiler(options),
|
new TemplateCompiler(options),
|
||||||
new StylesheetCompiler(),
|
new StylesheetCompiler(),
|
||||||
new DeferredRewriter(options)
|
],
|
||||||
]
|
[new DeferredRewriter(options)]
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
return new AngularTransformerGroup._(phases,
|
return new AngularTransformerGroup._(phases,
|
||||||
|
|
Loading…
Reference in New Issue