feat(transformers): directive aliases in Dart transformers (fix #1747)

This commit is contained in:
Sigmund Cherem
2015-07-23 15:25:32 -07:00
parent 46502e4d61
commit fd46b49ea6
43 changed files with 910 additions and 287 deletions
@@ -6,6 +6,9 @@ const REFLECTOR_VAR_NAME = 'reflector';
const TRANSFORM_DYNAMIC_MODE = 'transform_dynamic';
const DEPS_EXTENSION = '.ng_deps.dart';
const META_EXTENSION = '.ng_meta.json';
// TODO(sigmund): consider merging into .ng_meta by generating local metadata
// upfront (rather than extracting it from ng_deps).
const ALIAS_EXTENSION = '.aliases.json';
const REFLECTION_CAPABILITIES_NAME = 'ReflectionCapabilities';
const REGISTER_TYPE_METHOD_NAME = 'registerType';
const REGISTER_GETTERS_METHOD_NAME = 'registerGetters';
@@ -20,6 +23,10 @@ String toMetaExtension(String uri) =>
String toDepsExtension(String uri) =>
_toExtension(uri, const [META_EXTENSION, '.dart'], DEPS_EXTENSION);
/// Returns `uri` with its extension updated to [ALIAS_EXTENSION].
String toAliasExtension(String uri) =>
_toExtension(uri, const [DEPS_EXTENSION, '.dart'], ALIAS_EXTENSION);
/// Returns `uri` with its extension updated to `toExtension` if its
/// extension is currently in `fromExtension`.
String _toExtension(
@@ -0,0 +1,88 @@
library angular2.transform.common.ng_meta;
import 'package:angular2/src/render/api.dart';
import 'package:angular2/src/render/dom/convert.dart';
import 'logging.dart';
/// Metadata about directives and directive aliases.
///
/// [NgMeta] is used in three stages of the transformation process. First we
/// store directive aliases exported from a single file in an [NgMeta] instance.
/// Later we use another [NgMeta] instance to store more information about a
/// single file, including both directive aliases and directives extracted from
/// the corresponding `.ng_deps.dart` file. Further down the compilation
/// process, the template compiler needs to reason about the namespace of import
/// prefixes, so it will combine multple [NgMeta] instances together if they
/// were imported into a file with the same prefix.
///
/// Instances of this class are serialized into `.aliases.json` and
/// `.ng_meta.json` files as intermediate assets to make the compilation process
/// easier.
class NgMeta {
/// Directive metadata for each type annotated as a directive.
final Map<String, DirectiveMetadata> types;
/// List of other types and names associated with a given name.
final Map<String, List<String>> aliases;
NgMeta(this.types, this.aliases);
NgMeta.empty() : this({}, {});
bool get isEmpty => types.isEmpty && aliases.isEmpty;
/// Parse from the serialized form produced by [toJson].
factory NgMeta.fromJson(Map json) {
var types = {};
var aliases = {};
for (var key in json.keys) {
var entry = json[key];
if (entry['kind'] == 'type') {
types[key] = directiveMetadataFromMap(entry['value']);
} else if (entry['kind'] == 'alias') {
aliases[key] = entry['value'];
}
}
return new NgMeta(types, aliases);
}
/// Serialized representation of this instance.
Map toJson() {
var result = {};
types.forEach((k, v) {
result[k] = {'kind': 'type', 'value': directiveMetadataToMap(v)};
});
aliases.forEach((k, v) {
result[k] = {'kind': 'alias', 'value': v};
});
return result;
}
/// Merge into this instance all information from [other].
void addAll(NgMeta other) {
types.addAll(other.types);
aliases.addAll(other.aliases);
}
/// Returns the metadata for every type associated with the given [alias].
List<DirectiveMetadata> flatten(String alias) {
var result = [];
var seen = new Set();
helper(name) {
if (!seen.add(name)) {
logger.warning('Circular alias dependency for "$name".');
return;
}
if (types.containsKey(name)) {
result.add(types[name]);
} else if (aliases.containsKey(name)) {
aliases[name].forEach(helper);
} else {
logger.warning('Unknown alias: "$name".');
}
}
helper(alias);
return result;
}
}