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:
Tim Blasi 2015-10-13 16:38:00 -07:00
parent d68955ac6d
commit a18358d484
8 changed files with 186 additions and 365 deletions

View File

@ -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];
}
}

View File

@ -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();
}
types.forEach((k, v) {
result[k] = {_KIND_KEY: _TYPE_VALUE, _VALUE_KEY: v.toJson()};

View File

@ -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() {}';

View File

@ -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;
}
return constructorName.name == 'Component';
// 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);
}
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',
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;
}
retVal[rType] = visitor.compileData;
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.');
}
});
return new CompileDataResults._(ngDeps, retVal, directiveMetadatas);
}
compileData[reflectable] = compileDatum;
}
}
}
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.');
}
}
}
}

View File

@ -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);
}
}
AssetId templatesAssetId(AssetId ngDepsAssetId) =>
new AssetId(ngDepsAssetId.package, toTemplateExtension(ngDepsAssetId.path));
return new Outputs._(
viewDefResults.ngMeta.ngDeps, writeSourceModule(compiledTemplates));
}
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);
}

View File

@ -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) ';
}
});
}

View File

@ -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';

View File

@ -40,8 +40,8 @@ class AngularTransformerGroup extends TransformerGroup {
[
new TemplateCompiler(options),
new StylesheetCompiler(),
new DeferredRewriter(options)
]
],
[new DeferredRewriter(options)]
];
}
return new AngularTransformerGroup._(phases,