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) {
|
||||
var retVal = null;
|
||||
for (var variable in node.fields.variables) {
|
||||
var propModel = new PropertyMetadataModel()..name = '${variable.name}';
|
||||
var propModel = null;
|
||||
for (var meta in node.metadata) {
|
||||
var annotationModel = meta.accept(_annotationVisitor);
|
||||
if (annotationModel != null) {
|
||||
if (propModel == null) {
|
||||
propModel = new PropertyMetadataModel()..name = '${variable.name}';
|
||||
}
|
||||
propModel.annotations.add(annotationModel);
|
||||
}
|
||||
}
|
||||
if (propModel.annotations.isNotEmpty) {
|
||||
if (propModel != null && propModel.annotations.isNotEmpty) {
|
||||
if (retVal == null) {
|
||||
retVal = <PropertyMetadataModel>[];
|
||||
}
|
||||
|
@ -200,14 +203,17 @@ class _PropertyMetadataVisitor
|
|||
@override
|
||||
List<PropertyMetadataModel> visitMethodDeclaration(MethodDeclaration node) {
|
||||
if (node.isGetter || node.isSetter) {
|
||||
var propModel = new PropertyMetadataModel()..name = '${node.name}';
|
||||
var propModel = null;
|
||||
for (var meta in node.metadata) {
|
||||
var annotationModel = meta.accept(_annotationVisitor);
|
||||
if (annotationModel != null) {
|
||||
if (propModel == null) {
|
||||
propModel = new PropertyMetadataModel()..name = '${node.name}';
|
||||
}
|
||||
propModel.annotations.add(annotationModel);
|
||||
}
|
||||
}
|
||||
if (propModel.annotations.isNotEmpty) {
|
||||
if (propModel != null && propModel.annotations.isNotEmpty) {
|
||||
return <PropertyMetadataModel>[propModel];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -101,11 +101,9 @@ class NgMeta {
|
|||
}
|
||||
|
||||
/// Serialized representation of this instance.
|
||||
Map toJson({bool withNgDeps: true}) {
|
||||
Map toJson() {
|
||||
var result = {};
|
||||
if (withNgDeps) {
|
||||
result[_NG_DEPS_KEY] = isNgDepsEmpty ? null : ngDeps.writeToJsonMap();
|
||||
}
|
||||
result[_NG_DEPS_KEY] = isNgDepsEmpty ? null : ngDeps.writeToJsonMap();
|
||||
|
||||
types.forEach((k, v) {
|
||||
result[k] = {_KIND_KEY: _TYPE_VALUE, _VALUE_KEY: v.toJson()};
|
||||
|
|
|
@ -4,8 +4,6 @@ import 'dart:async';
|
|||
import 'dart:convert';
|
||||
|
||||
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/names.dart';
|
||||
import 'package:barback/barback.dart';
|
||||
|
@ -34,34 +32,14 @@ class DirectiveMetadataLinker extends Transformer {
|
|||
return linkDirectiveMetadata(
|
||||
new AssetReader.fromTransform(transform), primaryId).then((ngMeta) {
|
||||
if (ngMeta != null) {
|
||||
if (!ngMeta.types.isEmpty || !ngMeta.aliases.isEmpty) {
|
||||
if (!ngMeta.isEmpty) {
|
||||
transform.addOutput(new Asset.fromString(
|
||||
primaryId, _encoder.convert(ngMeta.toJson(withNgDeps: false))));
|
||||
primaryId, _encoder.convert(ngMeta.toJson())));
|
||||
} else {
|
||||
// Not outputting an asset could confuse barback.
|
||||
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:convert';
|
||||
|
||||
import 'package:analyzer/analyzer.dart';
|
||||
import 'package:angular2/src/core/compiler/directive_metadata.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/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/ng_deps.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:code_transformers/assets.dart';
|
||||
|
||||
/// 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.
|
||||
Future<CompileDataResults> createCompileData(
|
||||
AssetReader reader, AssetId assetId) async {
|
||||
return logElapsedAsync(() {
|
||||
return new _CompileDataCreator(reader, assetId).createCompileData();
|
||||
return logElapsedAsync(() async {
|
||||
return (await _CompileDataCreator.create(reader, assetId))
|
||||
.createCompileData();
|
||||
}, operationName: 'createCompileData', assetId: assetId);
|
||||
}
|
||||
|
||||
class CompileDataResults {
|
||||
final NgDeps ngDeps;
|
||||
final Map<RegisteredType,
|
||||
final NgMeta ngMeta;
|
||||
final Map<ReflectionInfoModel,
|
||||
NormalizedComponentWithViewDirectives> viewDefinitions;
|
||||
final List<CompileDirectiveMetadata> directiveMetadatas;
|
||||
|
||||
CompileDataResults._(
|
||||
this.ngDeps, this.viewDefinitions, this.directiveMetadatas);
|
||||
this.ngMeta, this.viewDefinitions, this.directiveMetadatas);
|
||||
}
|
||||
|
||||
// TODO(kegluenq): Improve this test.
|
||||
bool _isViewAnnotation(InstanceCreationExpression node) {
|
||||
var constructorName = node.constructorName.type.name;
|
||||
if (constructorName is PrefixedIdentifier) {
|
||||
constructorName = constructorName.identifier;
|
||||
class _PrefixedDirectiveName {
|
||||
final String prefix;
|
||||
final String directiveName;
|
||||
|
||||
_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) {
|
||||
var constructorName = node.constructorName.type.name;
|
||||
if (constructorName is PrefixedIdentifier) {
|
||||
constructorName = constructorName.identifier;
|
||||
String _directivesValue(ReflectionInfoModel model, bool test(AnnotationModel)) {
|
||||
final viewAnnotation = model.annotations.firstWhere(test, orElse: () => null);
|
||||
if (viewAnnotation == null) return null;
|
||||
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
|
||||
|
@ -58,37 +95,65 @@ bool _isComponentAnnotation(InstanceCreationExpression node) {
|
|||
class _CompileDataCreator {
|
||||
final AssetReader reader;
|
||||
final AssetId entryPoint;
|
||||
final Future<NgDeps> ngDepsFuture;
|
||||
final List<CompileDirectiveMetadata> directiveMetadatas = [];
|
||||
final NgMeta ngMeta;
|
||||
final directiveMetadatas = <CompileDirectiveMetadata>[];
|
||||
|
||||
_CompileDataCreator(AssetReader reader, AssetId entryPoint)
|
||||
: this.reader = reader,
|
||||
this.entryPoint = entryPoint,
|
||||
ngDepsFuture = NgDeps.parse(reader, entryPoint);
|
||||
_CompileDataCreator(this.reader, this.entryPoint, this.ngMeta);
|
||||
|
||||
static Future<_CompileDataCreator> create(
|
||||
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 {
|
||||
var ngDeps = await ngDepsFuture;
|
||||
if (ngDeps == null || ngDeps.reflectables == null) {
|
||||
return new CompileDataResults._(ngMeta, const {}, directiveMetadatas);
|
||||
}
|
||||
|
||||
var retVal = <RegisteredType, NormalizedComponentWithViewDirectives>{};
|
||||
var visitor = new _DirectiveDependenciesVisitor(await _extractNgMeta());
|
||||
ngDeps.registeredTypes.forEach((rType) {
|
||||
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}';
|
||||
final compileData =
|
||||
<ReflectionInfoModel, NormalizedComponentWithViewDirectives>{};
|
||||
final ngMetaMap = await _extractNgMeta();
|
||||
|
||||
if (ngMeta.types.containsKey(typeName)) {
|
||||
visitor.compileData.component = ngMeta.types[typeName];
|
||||
} else {
|
||||
logger.warning('Missing component "$typeName" in metadata map',
|
||||
asset: entryPoint);
|
||||
for (var reflectable in ngDeps.reflectables) {
|
||||
if (ngMeta.types.containsKey(reflectable.name)) {
|
||||
final compileDirectiveMetadata = ngMeta.types[reflectable.name];
|
||||
if (compileDirectiveMetadata.template != null) {
|
||||
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
|
||||
|
@ -96,27 +161,27 @@ class _CompileDataCreator {
|
|||
/// Unprefixed imports have `null` as their value. `entryPoint` is included
|
||||
/// in the map with no prefix.
|
||||
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 (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 */,
|
||||
errorOnAbsolute: false)] = prefix;
|
||||
for (ImportModel model in ngMeta.ngDeps.imports) {
|
||||
if (model.uri.endsWith('.dart') && !model.isNgDeps) {
|
||||
var prefix =
|
||||
model.prefix != null && model.prefix.isEmpty ? null : model.prefix;
|
||||
importAssetToPrefix[fromUri(resolver.resolve(baseUri, model.uri))] =
|
||||
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 [CompileDirectiveMetadata].
|
||||
/// imports and creates a map of prefix (or blank) to the
|
||||
/// associated [NgMeta] object.
|
||||
///
|
||||
/// 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
|
||||
/// 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
|
||||
/// build a map:
|
||||
///
|
||||
/// ```
|
||||
/// {
|
||||
/// "prefix.MyComponent": [CompileDirectiveMetadata for MyComponent],
|
||||
/// "prefix": [NgMeta with CompileDirectiveMetadata for MyComponent],
|
||||
/// ...<any other entries>...
|
||||
/// }
|
||||
/// ```
|
||||
|
@ -172,98 +237,3 @@ class _CompileDataCreator {
|
|||
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 '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/facade/lang.dart';
|
||||
import 'package:angular2/src/core/reflection/reflection.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/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/ng_compiler.dart';
|
||||
import 'package:angular2/src/transform/common/ng_deps.dart';
|
||||
import 'package:barback/barback.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import 'reflection/codegen.dart' as reg;
|
||||
import 'reflection/processor.dart' as reg;
|
||||
import 'reflection/reflection_capabilities.dart';
|
||||
import 'compile_data_creator.dart';
|
||||
|
@ -30,22 +28,24 @@ import 'compile_data_creator.dart';
|
|||
Future<Outputs> processTemplates(AssetReader reader, AssetId assetId,
|
||||
{bool reflectPropertiesAsAttributes: false}) async {
|
||||
var viewDefResults = await createCompileData(reader, assetId);
|
||||
var codegen = null;
|
||||
if (viewDefResults.directiveMetadatas.isNotEmpty) {
|
||||
var processor = new reg.Processor();
|
||||
viewDefResults.directiveMetadatas.forEach(processor.process);
|
||||
codegen = new reg.Codegen();
|
||||
codegen.generate(processor);
|
||||
viewDefResults.ngMeta.ngDeps.getters
|
||||
.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,
|
||||
changeDetectionConfig: new ChangeDetectorGenConfig(assertionsEnabled(),
|
||||
assertionsEnabled(), reflectPropertiesAsAttributes, false));
|
||||
|
||||
var ngDeps = viewDefResults.ngDeps;
|
||||
var compileData =
|
||||
final compileData =
|
||||
viewDefResults.viewDefinitions.values.toList(growable: false);
|
||||
if (compileData.isEmpty) {
|
||||
return new Outputs(assetId, ngDeps, codegen, null, null);
|
||||
return new Outputs._(viewDefResults.ngMeta.ngDeps, null);
|
||||
}
|
||||
|
||||
var savedReflectionCapabilities = reflector.reflectionCapabilities;
|
||||
|
@ -55,86 +55,30 @@ Future<Outputs> processTemplates(AssetReader reader, AssetId assetId,
|
|||
}, operationName: 'compileTemplatesCodegen', assetId: assetId);
|
||||
reflector.reflectionCapabilities = savedReflectionCapabilities;
|
||||
|
||||
return new Outputs(assetId, ngDeps, codegen, viewDefResults.viewDefinitions,
|
||||
compiledTemplates);
|
||||
if (compiledTemplates != null) {
|
||||
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) =>
|
||||
new AssetId(ngDepsAssetId.package, toTemplateExtension(ngDepsAssetId.path));
|
||||
AssetId ngDepsAssetId(AssetId primaryId) =>
|
||||
new AssetId(primaryId.package, toDepsExtension(primaryId.path));
|
||||
|
||||
AssetId templatesAssetId(AssetId primaryId) =>
|
||||
new AssetId(primaryId.package, toTemplateExtension(primaryId.path));
|
||||
|
||||
class Outputs {
|
||||
final String ngDepsCode;
|
||||
final NgDepsModel ngDeps;
|
||||
final String templatesCode;
|
||||
|
||||
Outputs._(this.ngDepsCode, 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();
|
||||
}
|
||||
Outputs._(this.ngDeps, this.templatesCode);
|
||||
}
|
||||
|
|
|
@ -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/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/names.dart';
|
||||
|
@ -24,7 +25,7 @@ class TemplateCompiler extends Transformer {
|
|||
TemplateCompiler(this.options);
|
||||
|
||||
@override
|
||||
bool isPrimary(AssetId id) => id.path.endsWith(DEPS_EXTENSION);
|
||||
bool isPrimary(AssetId id) => id.path.endsWith(META_EXTENSION);
|
||||
|
||||
@override
|
||||
Future apply(Transform transform) async {
|
||||
|
@ -34,19 +35,25 @@ class TemplateCompiler extends Transformer {
|
|||
var reader = new AssetReader.fromTransform(transform);
|
||||
var outputs = await processTemplates(reader, primaryId,
|
||||
reflectPropertiesAsAttributes: options.reflectPropertiesAsAttributes);
|
||||
var ngDepsCode = '';
|
||||
var ngDepsCode = _emptyNgDepsContents;
|
||||
var templatesCode = '';
|
||||
if (outputs != null) {
|
||||
if (outputs.ngDepsCode != null) {
|
||||
ngDepsCode = formatter.format(outputs.ngDepsCode);
|
||||
if (outputs.ngDeps != null) {
|
||||
final buf = new StringBuffer();
|
||||
final writer = new NgDepsWriter(buf);
|
||||
writer.writeNgDepsModel(outputs.ngDeps);
|
||||
ngDepsCode = formatter.format(buf.toString());
|
||||
}
|
||||
if (outputs.templatesCode != null) {
|
||||
templatesCode = formatter.format(outputs.templatesCode);
|
||||
}
|
||||
}
|
||||
transform.addOutput(new Asset.fromString(primaryId, ngDepsCode));
|
||||
transform.addOutput(
|
||||
new Asset.fromString(ngDepsAssetId(primaryId), ngDepsCode));
|
||||
transform.addOutput(
|
||||
new Asset.fromString(templatesAssetId(primaryId), templatesCode));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const _emptyNgDepsContents = 'initReflector() {}\n';
|
||||
|
|
|
@ -40,8 +40,8 @@ class AngularTransformerGroup extends TransformerGroup {
|
|||
[
|
||||
new TemplateCompiler(options),
|
||||
new StylesheetCompiler(),
|
||||
new DeferredRewriter(options)
|
||||
]
|
||||
],
|
||||
[new DeferredRewriter(options)]
|
||||
];
|
||||
}
|
||||
return new AngularTransformerGroup._(phases,
|
||||
|
|
Loading…
Reference in New Issue