refactor(dart/transform): Compose NgDepsModel & NgMeta phases
Link `NgDeps` & `NgMeta` data in the same phase to avoid unnecessary asset creation & reading. Remove `NgMeta#exports` and instead use `NgDeps#exports` to link `NgMeta` files in `ng_meta_linker.dart`.
This commit is contained in:
parent
75187d605b
commit
4ac29621f4
|
@ -8,16 +8,17 @@ import 'package:path/path.dart' as path;
|
|||
/// If `fromAbsolute` is specified, `importPath` may be a relative path,
|
||||
/// otherwise it is expected to be absolute.
|
||||
String writeImportUri(String importPath, {String prefix, String fromAbsolute}) {
|
||||
var urlResolver = const TransformerUrlResolver();
|
||||
var codegenImportPath;
|
||||
|
||||
var resolver = const TransformerUrlResolver();
|
||||
var importUri = resolver.toAssetScheme(Uri.parse(importPath));
|
||||
var importUri =
|
||||
toAssetScheme(Uri.parse(urlResolver.resolve(fromAbsolute, importPath)));
|
||||
if (_canPackageImport(importUri) ||
|
||||
fromAbsolute == null ||
|
||||
fromAbsolute.isEmpty) {
|
||||
codegenImportPath = _toPackageImport(importUri);
|
||||
} else {
|
||||
var moduleUri = resolver.toAssetScheme(Uri.parse(fromAbsolute));
|
||||
var moduleUri = toAssetScheme(Uri.parse(fromAbsolute));
|
||||
if (_canImportRelative(importUri, from: moduleUri)) {
|
||||
codegenImportPath = path.url.relative(importUri.toString(),
|
||||
from: path.dirname(moduleUri.toString()));
|
||||
|
|
|
@ -2,74 +2,123 @@ library angular2.transform.common.ng_meta;
|
|||
|
||||
import 'package:angular2/src/core/compiler/directive_metadata.dart';
|
||||
import 'logging.dart';
|
||||
import 'model/ng_deps_model.pb.dart';
|
||||
import 'url_resolver.dart' show isDartCoreUri;
|
||||
|
||||
/// Metadata about directives and directive aliases.
|
||||
/// Metadata about directives, directive aliases, and injectable values.
|
||||
///
|
||||
/// [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.
|
||||
/// [NgMeta] is used in three stages of the transformation process:
|
||||
///
|
||||
/// Instances of this class are serialized into `.aliases.json` and
|
||||
/// `.ng_meta.json` files as intermediate assets to make the compilation process
|
||||
/// easier.
|
||||
/// First we store directive aliases and types exported directly (that is, not
|
||||
/// via an `export` statement) from a single file in an [NgMeta] instance.
|
||||
///
|
||||
/// In the second phase, we perform two actions:
|
||||
/// 1. Incorporate all the data from [NgMeta] instances created by all
|
||||
/// files `exported` by the original file, such that all aliases and types
|
||||
/// visible when importing the original file are stored in its associated
|
||||
/// [NgMeta] instance.
|
||||
/// 2. Use the [NgDepsModel] to write Dart code registering all injectable
|
||||
/// values with the Angular 2 runtime reflection system.
|
||||
///
|
||||
/// Further down the compilation process, the template compiler needs to reason
|
||||
/// about the namespace of import prefixes, so it will combine multiple [NgMeta]
|
||||
/// instances together if they were imported into a file with the same prefix.
|
||||
///
|
||||
/// Instances of this class are serialized into `.ng_meta.json` files as
|
||||
/// intermediate assets to make the compilation process easier.
|
||||
class NgMeta {
|
||||
static const _ALIAS_VALUE = 'alias';
|
||||
static const _KIND_KEY = 'kind';
|
||||
static const _NG_DEPS_KEY = 'ngDeps';
|
||||
static const _TYPE_VALUE = 'type';
|
||||
static const _VALUE_KEY = 'value';
|
||||
|
||||
/// Directive metadata for each type annotated as a directive.
|
||||
final Map<String, CompileDirectiveMetadata> types;
|
||||
|
||||
/// List of other types and names associated with a given name.
|
||||
final Map<String, List<String>> aliases;
|
||||
|
||||
/// TODO(kegluneq): Once merged with NgDepsModel, use its exports.
|
||||
final List<String> exports;
|
||||
// The NgDeps generated from
|
||||
final NgDepsModel ngDeps;
|
||||
|
||||
NgMeta(this.types, this.aliases, this.exports);
|
||||
NgMeta(
|
||||
{Map<String, CompileDirectiveMetadata> types,
|
||||
Map<String, List<String>> aliases,
|
||||
this.ngDeps: null})
|
||||
: this.types = types != null ? types : {},
|
||||
this.aliases = aliases != null ? aliases : {};
|
||||
|
||||
NgMeta.empty() : this({}, {}, []);
|
||||
NgMeta.empty() : this();
|
||||
|
||||
bool get isEmpty => types.isEmpty && aliases.isEmpty && exports.isEmpty;
|
||||
// `model` can be an `ImportModel` or `ExportModel`.
|
||||
static bool _isDartImport(dynamic model) => isDartCoreUri(model.uri);
|
||||
|
||||
bool get isNgDepsEmpty {
|
||||
if (ngDeps == null) return true;
|
||||
// If this file imports only dart: libraries and does not define any
|
||||
// reflectables of its own, it doesn't need a .ng_deps.dart file.
|
||||
if (ngDeps.reflectables == null || ngDeps.reflectables.isEmpty) {
|
||||
if ((ngDeps.imports == null || ngDeps.imports.every(_isDartImport)) &&
|
||||
(ngDeps.exports == null || ngDeps.exports.every(_isDartImport))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool get isEmpty => types.isEmpty && aliases.isEmpty && isNgDepsEmpty;
|
||||
|
||||
/// Parse from the serialized form produced by [toJson].
|
||||
factory NgMeta.fromJson(Map json) {
|
||||
var exports = <String>[];
|
||||
var types = {};
|
||||
var aliases = {};
|
||||
var ngDeps = null;
|
||||
final types = {};
|
||||
final aliases = {};
|
||||
for (var key in json.keys) {
|
||||
if (key == '__exports__') {
|
||||
exports = json[key];
|
||||
if (key == _NG_DEPS_KEY) {
|
||||
var ngDepsJsonMap = json[key];
|
||||
if (ngDepsJsonMap == null) continue;
|
||||
if (ngDepsJsonMap is! Map) {
|
||||
logger.warning(
|
||||
'Unexpected value $ngDepsJsonMap for key "$key" in NgMeta.');
|
||||
continue;
|
||||
}
|
||||
ngDeps = new NgDepsModel()..mergeFromJsonMap(ngDepsJsonMap);
|
||||
} else {
|
||||
var entry = json[key];
|
||||
if (entry['kind'] == 'type') {
|
||||
types[key] = CompileDirectiveMetadata.fromJson(entry['value']);
|
||||
} else if (entry['kind'] == 'alias') {
|
||||
aliases[key] = entry['value'];
|
||||
if (entry is! Map) {
|
||||
logger.warning('Unexpected value $entry for key "$key" in NgMeta.');
|
||||
continue;
|
||||
}
|
||||
if (entry[_KIND_KEY] == _TYPE_VALUE) {
|
||||
types[key] = CompileDirectiveMetadata.fromJson(entry[_VALUE_KEY]);
|
||||
} else if (entry[_KIND_KEY] == _ALIAS_VALUE) {
|
||||
aliases[key] = entry[_VALUE_KEY];
|
||||
}
|
||||
}
|
||||
}
|
||||
return new NgMeta(types, aliases, exports);
|
||||
return new NgMeta(types: types, aliases: aliases, ngDeps: ngDeps);
|
||||
}
|
||||
|
||||
/// Serialized representation of this instance.
|
||||
Map toJson() {
|
||||
Map toJson({bool withNgDeps: true}) {
|
||||
var result = {};
|
||||
result['__exports__'] = exports;
|
||||
if (withNgDeps) {
|
||||
result[_NG_DEPS_KEY] = isNgDepsEmpty ? null : ngDeps.writeToJsonMap();
|
||||
}
|
||||
|
||||
types.forEach((k, v) {
|
||||
result[k] = {'kind': 'type', 'value': v.toJson()};
|
||||
result[k] = {_KIND_KEY: _TYPE_VALUE, _VALUE_KEY: v.toJson()};
|
||||
});
|
||||
|
||||
aliases.forEach((k, v) {
|
||||
result[k] = {'kind': 'alias', 'value': v};
|
||||
result[k] = {_KIND_KEY: _ALIAS_VALUE, _VALUE_KEY: v};
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Merge into this instance all information from [other].
|
||||
/// This does not include `exports`.
|
||||
/// This does not include `ngDeps`.
|
||||
void addAll(NgMeta other) {
|
||||
types.addAll(other.types);
|
||||
aliases.addAll(other.aliases);
|
||||
|
|
|
@ -5,9 +5,6 @@ import 'package:glob/glob.dart';
|
|||
import 'annotation_matcher.dart';
|
||||
import 'mirror_mode.dart';
|
||||
|
||||
/// See `optimizationPhases` below for an explanation.
|
||||
const DEFAULT_OPTIMIZATION_PHASES = 5;
|
||||
|
||||
const CUSTOM_ANNOTATIONS_PARAM = 'custom_annotations';
|
||||
const ENTRY_POINT_PARAM = 'entry_points';
|
||||
const FORMAT_CODE_PARAM = 'format_code';
|
||||
|
@ -41,15 +38,6 @@ class TransformerOptions {
|
|||
|
||||
final bool reflectPropertiesAsAttributes;
|
||||
|
||||
/// The number of phases to spend optimizing output size.
|
||||
/// Each additional phase adds time to the transformation but may decrease
|
||||
/// final output size. There is a limit beyond which this will no longer
|
||||
/// decrease size, that is, setting this to 20 may not decrease size any
|
||||
/// more than setting it to 10, but you will still pay an additional
|
||||
/// penalty in transformation time.
|
||||
/// The "correct" number of phases varies with the structure of the app.
|
||||
final int optimizationPhases;
|
||||
|
||||
/// Whether to format generated code.
|
||||
/// Code that is only modified will never be formatted because doing so may
|
||||
/// invalidate the source maps generated by `dart2js` and/or other tools.
|
||||
|
@ -62,7 +50,6 @@ class TransformerOptions {
|
|||
this.mirrorMode,
|
||||
this.initReflector,
|
||||
this.annotationMatcher,
|
||||
this.optimizationPhases,
|
||||
{this.reflectPropertiesAsAttributes,
|
||||
this.formatCode});
|
||||
|
||||
|
@ -71,24 +58,16 @@ class TransformerOptions {
|
|||
MirrorMode mirrorMode: MirrorMode.none,
|
||||
bool initReflector: true,
|
||||
List<ClassDescriptor> customAnnotationDescriptors: const [],
|
||||
int optimizationPhases: DEFAULT_OPTIMIZATION_PHASES,
|
||||
bool inlineViews: true,
|
||||
bool reflectPropertiesAsAttributes: true,
|
||||
bool formatCode: false}) {
|
||||
var annotationMatcher = new AnnotationMatcher()
|
||||
..addAll(customAnnotationDescriptors);
|
||||
optimizationPhases = optimizationPhases.isNegative ? 0 : optimizationPhases;
|
||||
var entryPointGlobs = entryPoints != null
|
||||
? entryPoints.map((path) => new Glob(path)).toList(growable: false)
|
||||
: null;
|
||||
return new TransformerOptions._internal(
|
||||
entryPoints,
|
||||
entryPointGlobs,
|
||||
modeName,
|
||||
mirrorMode,
|
||||
initReflector,
|
||||
annotationMatcher,
|
||||
optimizationPhases,
|
||||
return new TransformerOptions._internal(entryPoints, entryPointGlobs,
|
||||
modeName, mirrorMode, initReflector, annotationMatcher,
|
||||
reflectPropertiesAsAttributes: reflectPropertiesAsAttributes,
|
||||
formatCode: formatCode);
|
||||
}
|
||||
|
|
|
@ -28,14 +28,11 @@ TransformerOptions parseBarbackSettings(BarbackSettings settings) {
|
|||
mirrorMode = MirrorMode.none;
|
||||
break;
|
||||
}
|
||||
var optimizationPhases = _readInt(config, OPTIMIZATION_PHASES_PARAM,
|
||||
defaultValue: DEFAULT_OPTIMIZATION_PHASES);
|
||||
return new TransformerOptions(entryPoints,
|
||||
modeName: settings.mode.name,
|
||||
mirrorMode: mirrorMode,
|
||||
initReflector: initReflector,
|
||||
customAnnotationDescriptors: _readCustomAnnotations(config),
|
||||
optimizationPhases: optimizationPhases,
|
||||
reflectPropertiesAsAttributes: reflectPropertiesAsAttributes,
|
||||
formatCode: formatCode);
|
||||
}
|
||||
|
@ -68,18 +65,6 @@ List<String> _readFileList(Map config, String paramName) {
|
|||
return files;
|
||||
}
|
||||
|
||||
int _readInt(Map config, String paramName, {int defaultValue: null}) {
|
||||
if (!config.containsKey(paramName)) return defaultValue;
|
||||
var value = config[paramName];
|
||||
if (value is String) {
|
||||
value = int.parse(value);
|
||||
}
|
||||
if (value is! int) {
|
||||
throw new ArgumentError.value(value, paramName, 'Expected an integer');
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/// Parse the [CUSTOM_ANNOTATIONS_PARAM] options out of the transformer into
|
||||
/// [ClassDescriptor]s.
|
||||
List<ClassDescriptor> _readCustomAnnotations(Map config) {
|
||||
|
|
|
@ -1,57 +1,83 @@
|
|||
library angular2.transform.template_compiler.url_resolver;
|
||||
|
||||
import 'package:angular2/src/core/compiler/url_resolver.dart';
|
||||
import 'package:angular2/src/core/services.dart';
|
||||
import 'package:barback/barback.dart';
|
||||
|
||||
class TransformerUrlResolver implements UrlResolver {
|
||||
const TransformerUrlResolver();
|
||||
|
||||
@override
|
||||
String resolve(String baseUrl, String url) {
|
||||
if (url == null) throw new ArgumentError.notNull('url');
|
||||
Uri uri = Uri.parse(url);
|
||||
|
||||
if (!uri.isAbsolute) {
|
||||
if (baseUrl == null) throw new ArgumentError.notNull('baseUrl');
|
||||
if (baseUrl.isEmpty) throw new ArgumentError.value(
|
||||
'(empty string)', 'baseUrl');
|
||||
uri = Uri.parse(baseUrl).resolveUri(uri);
|
||||
}
|
||||
|
||||
return toAssetScheme(uri).toString();
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts `absoluteUri` to use the 'asset' scheme used in the Angular 2
|
||||
/// template compiler.
|
||||
///
|
||||
/// The `scheme` of `absoluteUri` is expected to be either 'package' or
|
||||
/// 'asset'.
|
||||
Uri toAssetScheme(Uri absoluteUri) {
|
||||
if (absoluteUri == null) return null;
|
||||
String toAssetUri(AssetId assetId) {
|
||||
if (assetId == null) throw new ArgumentError.notNull('assetId');
|
||||
return 'asset:${assetId.package}/${assetId.path}';
|
||||
}
|
||||
|
||||
if (!absoluteUri.isAbsolute) {
|
||||
throw new ArgumentError.value(
|
||||
absoluteUri, 'absoluteUri', 'Value passed must be an absolute uri');
|
||||
}
|
||||
if (absoluteUri.scheme == 'asset') {
|
||||
if (absoluteUri.pathSegments.length < 3) {
|
||||
throw new FormatException(
|
||||
'An asset: URI must have at least 3 path '
|
||||
'segments, for example '
|
||||
'asset:<package-name>/<first-level-dir>/<path-to-dart-file>.',
|
||||
absoluteUri);
|
||||
}
|
||||
return absoluteUri;
|
||||
}
|
||||
if (absoluteUri.scheme != 'package') {
|
||||
throw new ArgumentError.value(
|
||||
absoluteUri, 'absoluteUri', 'Unsupported URI scheme encountered');
|
||||
}
|
||||
AssetId fromUri(String assetUri) {
|
||||
if (assetUri == null) throw new ArgumentError.notNull('assetUri');
|
||||
if (assetUri.isEmpty) throw new ArgumentError.value(
|
||||
'(empty string)', 'assetUri');
|
||||
var uri = toAssetScheme(Uri.parse(assetUri));
|
||||
return new AssetId(
|
||||
uri.pathSegments.first, uri.pathSegments.skip(1).join('/'));
|
||||
}
|
||||
|
||||
if (absoluteUri.pathSegments.length < 2) {
|
||||
/// Converts `absoluteUri` to use the 'asset' scheme used in the Angular 2
|
||||
/// template compiler.
|
||||
///
|
||||
/// The `scheme` of `absoluteUri` is expected to be either 'package' or
|
||||
/// 'asset'.
|
||||
Uri toAssetScheme(Uri absoluteUri) {
|
||||
if (absoluteUri == null) throw new ArgumentError.notNull('absoluteUri');
|
||||
|
||||
if (!absoluteUri.isAbsolute) {
|
||||
throw new ArgumentError.value(
|
||||
absoluteUri, 'absoluteUri', 'Value passed must be an absolute uri');
|
||||
}
|
||||
if (absoluteUri.scheme == 'asset') {
|
||||
if (absoluteUri.pathSegments.length < 3) {
|
||||
throw new FormatException(
|
||||
'A package: URI must have at least 2 path '
|
||||
'An asset: URI must have at least 3 path '
|
||||
'segments, for example '
|
||||
'package:<package-name>/<path-to-dart-file>',
|
||||
'asset:<package-name>/<first-level-dir>/<path-to-dart-file>.',
|
||||
absoluteUri);
|
||||
}
|
||||
|
||||
var pathSegments = absoluteUri.pathSegments.toList()..insert(1, 'lib');
|
||||
return new Uri(scheme: 'asset', pathSegments: pathSegments);
|
||||
return absoluteUri;
|
||||
}
|
||||
if (absoluteUri.scheme != 'package') {
|
||||
throw new FormatException(
|
||||
'Unsupported URI scheme "${absoluteUri.scheme}" encountered.',
|
||||
absoluteUri);
|
||||
}
|
||||
|
||||
if (absoluteUri.pathSegments.length < 2) {
|
||||
throw new FormatException(
|
||||
'A package: URI must have at least 2 path '
|
||||
'segments, for example '
|
||||
'package:<package-name>/<path-to-dart-file>',
|
||||
absoluteUri);
|
||||
}
|
||||
|
||||
var pathSegments = absoluteUri.pathSegments.toList()..insert(1, 'lib');
|
||||
return new Uri(scheme: 'asset', pathSegments: pathSegments);
|
||||
}
|
||||
|
||||
bool isDartCoreUri(String uri) {
|
||||
if (uri == null) throw new ArgumentError.notNull('uri');
|
||||
if (uri.isEmpty) throw new ArgumentError.value('(empty string)', 'uri');
|
||||
return uri.startsWith('dart:');
|
||||
}
|
||||
|
|
|
@ -3,20 +3,25 @@ library angular2.transform.template_compiler.xhr_impl;
|
|||
import 'dart:async';
|
||||
import 'package:angular2/src/core/compiler/xhr.dart' show XHR;
|
||||
import 'package:angular2/src/transform/common/asset_reader.dart';
|
||||
import 'package:barback/barback.dart';
|
||||
import 'package:angular2/src/transform/common/logging.dart';
|
||||
import 'package:angular2/src/transform/common/url_resolver.dart';
|
||||
|
||||
/// Transformer-specific implementation of XHR that is backed by an
|
||||
/// [AssetReader].
|
||||
///
|
||||
/// This implementation expects urls using the asset: scheme.
|
||||
/// See [src/transform/common/url_resolver.dart] for a way to convert package:
|
||||
/// and relative urls to asset: urls.
|
||||
class XhrImpl implements XHR {
|
||||
final AssetReader _reader;
|
||||
|
||||
XhrImpl(this._reader);
|
||||
|
||||
Future<String> get(String url) async {
|
||||
final uri = Uri.parse(url);
|
||||
if (uri.scheme != 'asset') {
|
||||
throw new FormatException('Unsupported uri encountered: $uri', url);
|
||||
final assetId = fromUri(url);
|
||||
if (!url.startsWith('asset:')) {
|
||||
logger.warning('XhrImpl received unexpected url: $url');
|
||||
}
|
||||
final assetId =
|
||||
new AssetId(uri.pathSegments.first, uri.pathSegments.skip(1).join('/'));
|
||||
|
||||
if (!await _reader.hasInput(assetId)) {
|
||||
throw new ArgumentError.value('Could not read asset at uri $url', 'url');
|
||||
|
|
|
@ -1,118 +0,0 @@
|
|||
library angular2.transform.directive_linker.linker;
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:angular2/src/transform/common/asset_reader.dart';
|
||||
import 'package:angular2/src/transform/common/logging.dart';
|
||||
import 'package:angular2/src/transform/common/names.dart';
|
||||
import 'package:angular2/src/transform/common/model/import_export_model.pb.dart';
|
||||
import 'package:angular2/src/transform/common/model/ng_deps_model.pb.dart';
|
||||
import 'package:barback/barback.dart';
|
||||
import 'package:code_transformers/assets.dart';
|
||||
|
||||
/// Checks the `.ng_deps.json` file represented by `entryPoint` and
|
||||
/// determines whether it is necessary to the functioning of the Angular 2
|
||||
/// Dart app.
|
||||
///
|
||||
/// An `.ng_deps.json` file is not necessary if:
|
||||
/// 1. It does not register any `@Injectable` types with the system.
|
||||
/// 2. It does not import any libraries whose `.ng_deps.json` files register
|
||||
/// any `@Injectable` types with the system.
|
||||
///
|
||||
/// Since `@Directive` and `@Component` inherit from `@Injectable`, we know
|
||||
/// we will not miss processing any classes annotated with those tags.
|
||||
Future<bool> isNecessary(AssetReader reader, AssetId entryPoint) async {
|
||||
if (!(await reader.hasInput(entryPoint))) return false;
|
||||
var jsonString = await reader.readAsString(entryPoint);
|
||||
if (jsonString == null || jsonString.isEmpty) return false;
|
||||
var ngDepsModel = new NgDepsModel.fromJson(jsonString);
|
||||
|
||||
if (ngDepsModel.reflectables != null &&
|
||||
ngDepsModel.reflectables.isNotEmpty) return true;
|
||||
|
||||
// We do not register any @Injectables, do we call any dependencies?
|
||||
var linkedDepsMap = await _processNgImports(reader, entryPoint, ngDepsModel);
|
||||
return linkedDepsMap.isNotEmpty;
|
||||
}
|
||||
|
||||
/// Modifies the [NgDepsModel] represented by `entryPoint` to import its
|
||||
/// dependencies' associated `.ng_deps.dart` files.
|
||||
///
|
||||
/// For example, if entry_point.ng_deps.dart imports dependency.dart, this
|
||||
/// will check if dependency.ng_deps.json exists. If it does, we add an import
|
||||
/// to dependency.ng_deps.dart to the entry_point [NgDepsModel] and set
|
||||
/// `isNgDeps` to `true` to signify that it is a dependency on which we need to
|
||||
/// call `initReflector`.
|
||||
Future<NgDepsModel> linkNgDeps(AssetReader reader, AssetId entryPoint) async {
|
||||
if (!(await reader.hasInput(entryPoint))) return null;
|
||||
var jsonString = await reader.readAsString(entryPoint);
|
||||
if (jsonString.isEmpty) return null;
|
||||
var ngDepsModel = new NgDepsModel.fromJson(jsonString);
|
||||
|
||||
var linkedDepsMap = await _processNgImports(reader, entryPoint, ngDepsModel);
|
||||
|
||||
if (linkedDepsMap.isEmpty) {
|
||||
// We are not calling `initReflector` on any other libraries, but we still
|
||||
// return the model to ensure it is written to code.
|
||||
// TODO(kegluneq): Continue using the protobuf format after this phase.
|
||||
return ngDepsModel;
|
||||
}
|
||||
|
||||
for (var i = ngDepsModel.imports.length - 1; i >= 0; --i) {
|
||||
var import = ngDepsModel.imports[i];
|
||||
if (linkedDepsMap.containsKey(import.uri)) {
|
||||
var linkedModel = new ImportModel()
|
||||
..isNgDeps = true
|
||||
..uri = toDepsExtension(linkedDepsMap[import.uri])
|
||||
..prefix = 'i$i';
|
||||
// TODO(kegluneq): Preserve combinators?
|
||||
ngDepsModel.imports.insert(i + 1, linkedModel);
|
||||
}
|
||||
}
|
||||
for (var i = 0, iLen = ngDepsModel.exports.length; i < iLen; ++i) {
|
||||
var export = ngDepsModel.exports[i];
|
||||
if (linkedDepsMap.containsKey(export.uri)) {
|
||||
var linkedModel = new ImportModel()
|
||||
..isNgDeps = true
|
||||
..uri = toDepsExtension(linkedDepsMap[export.uri])
|
||||
..prefix = 'i${ngDepsModel.imports.length}';
|
||||
// TODO(kegluneq): Preserve combinators?
|
||||
ngDepsModel.imports.add(linkedModel);
|
||||
}
|
||||
}
|
||||
|
||||
return ngDepsModel;
|
||||
}
|
||||
|
||||
bool _isNotDartDirective(dynamic model) {
|
||||
return !model.uri.startsWith('dart:');
|
||||
}
|
||||
|
||||
/// Maps the `uri` of each input [ImportModel] or [ExportModel] to its
|
||||
/// associated `.ng_deps.json` file, if one exists.
|
||||
Future<Map<String, String>> _processNgImports(
|
||||
AssetReader reader, AssetId ngJsonAsset, NgDepsModel model) {
|
||||
final nullFuture = new Future.value(null);
|
||||
final importsAndExports = new List.from(model.imports)..addAll(model.exports);
|
||||
final retVal = <String, String>{};
|
||||
final entryPoint =
|
||||
new AssetId(ngJsonAsset.package, toDepsExtension(ngJsonAsset.path));
|
||||
return Future
|
||||
.wait(
|
||||
importsAndExports.where(_isNotDartDirective).map((dynamic directive) {
|
||||
// The uri of the import or export with .dart replaced with .ng_deps.json.
|
||||
// This is the json file containing Angular 2 codegen info, if one exists.
|
||||
var linkedJsonUri = toJsonExtension(directive.uri);
|
||||
var spanArg = null;
|
||||
var linkedNgJsonAsset = uriToAssetId(
|
||||
entryPoint, linkedJsonUri, logger, spanArg,
|
||||
errorOnAbsolute: false);
|
||||
if (linkedNgJsonAsset == ngJsonAsset) return nullFuture;
|
||||
return reader.hasInput(linkedNgJsonAsset).then((hasInput) {
|
||||
if (hasInput) {
|
||||
retVal[directive.uri] = linkedJsonUri;
|
||||
}
|
||||
}, onError: (_) => null);
|
||||
}))
|
||||
.then((_) => retVal);
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
library angular2.transform.directive_linker.transformer;
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
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';
|
||||
|
||||
import 'linker.dart';
|
||||
|
||||
/// Transformer responsible for processing `.ng_deps.json` files created by
|
||||
/// {@link DirectiveProcessor} and ensuring that each imports its dependencies'
|
||||
/// .ng_deps.dart files.
|
||||
class DirectiveLinker extends Transformer implements DeclaringTransformer {
|
||||
DirectiveLinker();
|
||||
|
||||
@override
|
||||
bool isPrimary(AssetId id) => id.path.endsWith(DEPS_JSON_EXTENSION);
|
||||
|
||||
@override
|
||||
declareOutputs(DeclaringTransform transform) {
|
||||
// TODO(kegluenq): We should consume this, but doing so causes barback to
|
||||
// incorrectly determine what assets are available in this phase.
|
||||
// transform.consumePrimary();
|
||||
transform.declareOutput(_depsAssetId(transform.primaryId));
|
||||
}
|
||||
|
||||
@override
|
||||
Future apply(Transform transform) async {
|
||||
await log.initZoned(transform, () async {
|
||||
var reader = new AssetReader.fromTransform(transform);
|
||||
var primaryId = transform.primaryInput.id;
|
||||
var ngDepsModel = await linkNgDeps(reader, primaryId);
|
||||
// See above
|
||||
// transform.consumePrimary();
|
||||
var outputAssetId = _depsAssetId(primaryId);
|
||||
if (ngDepsModel != null) {
|
||||
var buf = new StringBuffer();
|
||||
var writer = new NgDepsWriter(buf);
|
||||
writer.writeNgDepsModel(ngDepsModel);
|
||||
var formattedCode = formatter.format('$buf', uri: primaryId.path);
|
||||
transform.addOutput(new Asset.fromString(outputAssetId, formattedCode));
|
||||
} else {
|
||||
transform.addOutput(new Asset.fromString(outputAssetId, ''));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
AssetId _depsAssetId(AssetId primaryId) =>
|
||||
new AssetId(primaryId.package, toDepsExtension(primaryId.path));
|
||||
|
||||
/// Transformer responsible for removing unnecessary `.ng_deps.json` files
|
||||
/// created by {@link DirectiveProcessor}.
|
||||
class EmptyNgDepsRemover extends Transformer implements DeclaringTransformer {
|
||||
EmptyNgDepsRemover();
|
||||
|
||||
@override
|
||||
bool isPrimary(AssetId id) => id.path.endsWith(DEPS_JSON_EXTENSION);
|
||||
|
||||
/// We occasionally consume the primary input, but that depends on the
|
||||
/// contents of the file, so we conservatively declare that we both consume
|
||||
/// and output the asset. This prevents barback from making any assumptions
|
||||
/// about the existence of the assets until after the transformer has run.
|
||||
@override
|
||||
declareOutputs(DeclaringTransform transform) {
|
||||
transform.consumePrimary();
|
||||
transform.declareOutput(transform.primaryId);
|
||||
}
|
||||
|
||||
@override
|
||||
Future apply(Transform transform) async {
|
||||
await log.initZoned(transform, () async {
|
||||
var reader = new AssetReader.fromTransform(transform);
|
||||
transform.consumePrimary();
|
||||
if ((await isNecessary(reader, transform.primaryInput.id))) {
|
||||
transform.addOutput(transform.primaryInput);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
library angular2.transform.directive_metadata_linker.linker;
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:angular2/src/transform/common/asset_reader.dart';
|
||||
import 'package:angular2/src/transform/common/logging.dart';
|
||||
import 'package:angular2/src/transform/common/names.dart';
|
||||
import 'package:angular2/src/transform/common/ng_meta.dart';
|
||||
import 'package:barback/barback.dart';
|
||||
import 'package:code_transformers/assets.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
/// Returns [NgMeta] associated with [entryPoint] combined with the [NgMeta] of
|
||||
/// all files `export`ed from the original file.
|
||||
///
|
||||
/// This includes entries for every `Directive`-annotated class and
|
||||
/// constants that match the directive-aliases pattern.
|
||||
///
|
||||
/// There are entries for each of these which is visible from a file importing
|
||||
/// the original .dart file that produced `entryPoint`. That is, this includes
|
||||
/// all `Directive` annotated public classes in that file, all `DirectiveAlias`
|
||||
/// annotated public variables, and any of those entries which are visible from
|
||||
/// files which the .dart file `export`ed.
|
||||
///
|
||||
/// Returns an empty [NgMeta] if there are no `Directive`-annotated classes or
|
||||
/// `DirectiveAlias` annotated constants in `entryPoint`.
|
||||
Future<NgMeta> linkDirectiveMetadata(AssetReader reader, AssetId entryPoint) {
|
||||
return _linkDirectiveMetadataRecursive(
|
||||
reader, entryPoint, new Set<AssetId>());
|
||||
}
|
||||
|
||||
final _nullFuture = new Future.value(null);
|
||||
|
||||
// TODO(kegluneq): Don't reinvent the wheel? Centalize?
|
||||
AssetId _fromPackageUri(String packageUri) {
|
||||
var pathParts = path.url.split(packageUri);
|
||||
return new AssetId(pathParts[0].substring('package:'.length),
|
||||
'lib/${pathParts.getRange(1, pathParts.length).join('/')}');
|
||||
}
|
||||
|
||||
Future<NgMeta> _linkDirectiveMetadataRecursive(
|
||||
AssetReader reader, AssetId entryPoint, Set<AssetId> seen) async {
|
||||
if (entryPoint == null) {
|
||||
return new NgMeta.empty();
|
||||
}
|
||||
// Break cycles, if they exist.
|
||||
if (seen.contains(entryPoint)) return _nullFuture;
|
||||
seen.add(entryPoint);
|
||||
if (!(await reader.hasInput(entryPoint))) return new NgMeta.empty();
|
||||
|
||||
var ngMetaJson = await reader.readAsString(entryPoint);
|
||||
if (ngMetaJson == null || ngMetaJson.isEmpty) return new NgMeta.empty();
|
||||
|
||||
var ngMeta = new NgMeta.fromJson(JSON.decode(ngMetaJson));
|
||||
|
||||
if (ngMeta.exports == null) return ngMeta;
|
||||
|
||||
// Recursively add NgMeta files from `exports`.
|
||||
return Future.wait(ngMeta.exports.map((uri) {
|
||||
if (uri.startsWith('dart:')) return _nullFuture;
|
||||
var metaUri = toMetaExtension(uri);
|
||||
var assetId;
|
||||
if (uri.startsWith('package:')) {
|
||||
assetId = _fromPackageUri(metaUri);
|
||||
} else {
|
||||
assetId = uriToAssetId(entryPoint, metaUri, logger, null /* span */,
|
||||
errorOnAbsolute: false);
|
||||
}
|
||||
|
||||
return _linkDirectiveMetadataRecursive(reader, assetId, seen)
|
||||
.then((exportedNgMeta) {
|
||||
if (exportedNgMeta != null) {
|
||||
ngMeta.addAll(exportedNgMeta);
|
||||
}
|
||||
});
|
||||
})).then((_) => ngMeta);
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
library angular2.transform.directive_metadata_linker.ng_deps_linker;
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:angular2/src/core/services.dart';
|
||||
import 'package:angular2/src/transform/common/asset_reader.dart';
|
||||
import 'package:angular2/src/transform/common/logging.dart';
|
||||
import 'package:angular2/src/transform/common/names.dart';
|
||||
import 'package:angular2/src/transform/common/model/import_export_model.pb.dart';
|
||||
import 'package:angular2/src/transform/common/model/ng_deps_model.pb.dart';
|
||||
import 'package:angular2/src/transform/common/url_resolver.dart';
|
||||
import 'package:barback/barback.dart';
|
||||
|
||||
/// Modifies the [NgDepsModel] represented by `entryPoint` to import its
|
||||
/// dependencies' associated `.ng_deps.dart` files.
|
||||
///
|
||||
/// For example, if entry_point.ng_deps.dart imports dependency.dart, this
|
||||
/// will check if dependency.ng_meta.json exists. If it does, we add an import
|
||||
/// to dependency.ng_deps.dart to the entry_point [NgDepsModel] and set
|
||||
/// `isNgDeps` to `true` to signify that it is a dependency on which we need to
|
||||
/// call `initReflector`.
|
||||
Future<NgDepsModel> linkNgDeps(NgDepsModel ngDepsModel, AssetReader reader,
|
||||
AssetId entryPoint, UrlResolver resolver) async {
|
||||
if (ngDepsModel == null) return null;
|
||||
var linkedDepsMap =
|
||||
await _processNgImports(ngDepsModel, reader, entryPoint, resolver);
|
||||
|
||||
if (linkedDepsMap.isEmpty) {
|
||||
// We are not calling `initReflector` on any other libraries, but we still
|
||||
// return the model to ensure it is written to code.
|
||||
// TODO(kegluneq): Continue using the protobuf format after this phase.
|
||||
return ngDepsModel;
|
||||
}
|
||||
|
||||
for (var i = ngDepsModel.imports.length - 1; i >= 0; --i) {
|
||||
var import = ngDepsModel.imports[i];
|
||||
if (linkedDepsMap.containsKey(import.uri)) {
|
||||
var linkedModel = new ImportModel()
|
||||
..isNgDeps = true
|
||||
..uri = toDepsExtension(import.uri)
|
||||
..prefix = 'i$i';
|
||||
// TODO(kegluneq): Preserve combinators?
|
||||
ngDepsModel.imports.insert(i + 1, linkedModel);
|
||||
}
|
||||
}
|
||||
for (var i = 0, iLen = ngDepsModel.exports.length; i < iLen; ++i) {
|
||||
var export = ngDepsModel.exports[i];
|
||||
if (linkedDepsMap.containsKey(export.uri)) {
|
||||
var linkedModel = new ImportModel()
|
||||
..isNgDeps = true
|
||||
..uri = toDepsExtension(export.uri)
|
||||
..prefix = 'i${ngDepsModel.imports.length}';
|
||||
// TODO(kegluneq): Preserve combinators?
|
||||
ngDepsModel.imports.add(linkedModel);
|
||||
}
|
||||
}
|
||||
|
||||
return ngDepsModel;
|
||||
}
|
||||
|
||||
bool _isNotDartDirective(dynamic model) => !isDartCoreUri(model.uri);
|
||||
|
||||
/// Maps the `uri` of each input [ImportModel] or [ExportModel] to its
|
||||
/// associated `.ng_deps.json` file, if one exists.
|
||||
Future<Map<String, String>> _processNgImports(NgDepsModel model,
|
||||
AssetReader reader, AssetId assetId, UrlResolver resolver) async {
|
||||
final importsAndExports = new List.from(model.imports)..addAll(model.exports);
|
||||
final retVal = <String, String>{};
|
||||
final assetUri = toAssetUri(assetId);
|
||||
return Future
|
||||
.wait(
|
||||
importsAndExports.where(_isNotDartDirective).map((dynamic directive) {
|
||||
// The uri of the import or export with .dart replaced with .ng_meta.json.
|
||||
// This is the json file containing Angular 2 codegen info, if one exists.
|
||||
var linkedJsonUri =
|
||||
resolver.resolve(assetUri, toMetaExtension(directive.uri));
|
||||
return reader.hasInput(fromUri(linkedJsonUri)).then((hasInput) {
|
||||
if (hasInput) {
|
||||
retVal[directive.uri] = linkedJsonUri;
|
||||
}
|
||||
}, onError: (err, stack) {
|
||||
logger.warning('Error while looking for $linkedJsonUri. '
|
||||
'Message: $err\n'
|
||||
'Stack: $stack');
|
||||
});
|
||||
}))
|
||||
.then((_) => retVal);
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
library angular2.transform.directive_metadata_linker.linker;
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:angular2/src/transform/common/asset_reader.dart';
|
||||
import 'package:angular2/src/transform/common/logging.dart';
|
||||
import 'package:angular2/src/transform/common/names.dart';
|
||||
import 'package:angular2/src/transform/common/ng_meta.dart';
|
||||
import 'package:angular2/src/transform/common/url_resolver.dart';
|
||||
import 'package:barback/barback.dart';
|
||||
|
||||
import 'ng_deps_linker.dart';
|
||||
|
||||
/// Returns [NgMeta] associated with [entryPoint] combined with the [NgMeta] of
|
||||
/// all files `export`ed from the original file.
|
||||
///
|
||||
/// This includes entries for every `Directive`-annotated class and
|
||||
/// constants that match the directive-aliases pattern.
|
||||
///
|
||||
/// There are entries for each of these which is visible from a file importing
|
||||
/// the original .dart file that produced `entryPoint`. That is, this includes
|
||||
/// all `Directive` annotated public classes in that file, all `DirectiveAlias`
|
||||
/// annotated public variables, and any of those entries which are visible from
|
||||
/// files which the .dart file `export`ed.
|
||||
///
|
||||
/// Returns an empty [NgMeta] if there are no `Directive`-annotated classes or
|
||||
/// `DirectiveAlias` annotated constants in `entryPoint`.
|
||||
Future<NgMeta> linkDirectiveMetadata(
|
||||
AssetReader reader, AssetId entryPoint) async {
|
||||
var ngMeta = await _readNgMeta(reader, entryPoint);
|
||||
if (ngMeta == null || ngMeta.isEmpty) return null;
|
||||
|
||||
await Future.wait([
|
||||
linkNgDeps(ngMeta.ngDeps, reader, entryPoint, _urlResolver),
|
||||
_linkDirectiveMetadataRecursive(
|
||||
ngMeta, reader, entryPoint, new Set<String>())
|
||||
]);
|
||||
return ngMeta;
|
||||
}
|
||||
|
||||
Future<NgMeta> _readNgMeta(AssetReader reader, AssetId ngMetaAssetId) async {
|
||||
if (!(await reader.hasInput(ngMetaAssetId))) return null;
|
||||
|
||||
var ngMetaJson = await reader.readAsString(ngMetaAssetId);
|
||||
if (ngMetaJson == null || ngMetaJson.isEmpty) return null;
|
||||
|
||||
return new NgMeta.fromJson(JSON.decode(ngMetaJson));
|
||||
}
|
||||
|
||||
final _urlResolver = const TransformerUrlResolver();
|
||||
|
||||
Future<NgMeta> _linkDirectiveMetadataRecursive(NgMeta ngMeta,
|
||||
AssetReader reader, AssetId assetId, Set<String> seen) async {
|
||||
if (ngMeta == null ||
|
||||
ngMeta.ngDeps == null ||
|
||||
ngMeta.ngDeps.exports == null) {
|
||||
return ngMeta;
|
||||
}
|
||||
var assetUri = toAssetUri(assetId);
|
||||
|
||||
return Future
|
||||
.wait(ngMeta.ngDeps.exports
|
||||
.where((export) => !isDartCoreUri(export.uri))
|
||||
.map((export) =>
|
||||
_urlResolver.resolve(assetUri, toMetaExtension(export.uri)))
|
||||
.where((uri) => !seen.contains(uri))
|
||||
.map((uri) async {
|
||||
seen.add(uri);
|
||||
try {
|
||||
final exportAssetId = fromUri(uri);
|
||||
if (await reader.hasInput(exportAssetId)) {
|
||||
var exportNgMetaJson = await reader.readAsString(exportAssetId);
|
||||
if (exportNgMetaJson == null) return null;
|
||||
var exportNgMeta = new NgMeta.fromJson(JSON.decode(exportNgMetaJson));
|
||||
await _linkDirectiveMetadataRecursive(
|
||||
exportNgMeta, reader, exportAssetId, seen);
|
||||
if (exportNgMeta != null) {
|
||||
ngMeta.addAll(exportNgMeta);
|
||||
}
|
||||
}
|
||||
} catch (err, st) {
|
||||
// Log and continue.
|
||||
logger.warning('Failed to fetch $uri. Message: $err.\n$st');
|
||||
}
|
||||
}))
|
||||
.then((_) => ngMeta);
|
||||
}
|
|
@ -4,11 +4,13 @@ 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';
|
||||
|
||||
import 'linker.dart';
|
||||
import 'ng_meta_linker.dart';
|
||||
|
||||
/// Transformer responsible for processing .ng_meta.json files created by
|
||||
/// {@link DirectiveProcessor} and "linking" them.
|
||||
|
@ -31,6 +33,7 @@ class DirectiveMetadataLinker extends Transformer
|
|||
// incorrectly determine what assets are available in this phase.
|
||||
// transform.consumePrimary();
|
||||
transform.declareOutput(transform.primaryId);
|
||||
transform.declareOutput(_depsAssetId(transform.primaryId));
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -42,15 +45,32 @@ class DirectiveMetadataLinker extends Transformer
|
|||
new AssetReader.fromTransform(transform), primaryId).then((ngMeta) {
|
||||
// See above
|
||||
// transform.consumePrimary();
|
||||
if (ngMeta != null && !ngMeta.isEmpty) {
|
||||
transform.addOutput(new Asset.fromString(
|
||||
primaryId, _encoder.convert(ngMeta.toJson())));
|
||||
} else {
|
||||
// Not outputting an asset could confuse barback, so output an
|
||||
// empty one.
|
||||
transform.addOutput(transform.primaryInput);
|
||||
if (ngMeta != null) {
|
||||
if (!ngMeta.types.isEmpty || !ngMeta.aliases.isEmpty) {
|
||||
transform.addOutput(new Asset.fromString(
|
||||
primaryId, _encoder.convert(ngMeta.toJson(withNgDeps: false))));
|
||||
} 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, ''));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
AssetId _depsAssetId(AssetId primaryId) =>
|
||||
new AssetId(primaryId.package, toDepsExtension(primaryId.path));
|
||||
|
|
|
@ -9,7 +9,6 @@ import 'package:angular2/src/transform/common/code/ng_deps_code.dart';
|
|||
import 'package:angular2/src/transform/common/directive_metadata_reader.dart';
|
||||
import 'package:angular2/src/transform/common/interface_matcher.dart';
|
||||
import 'package:angular2/src/transform/common/logging.dart';
|
||||
import 'package:angular2/src/transform/common/model/ng_deps_model.pb.dart';
|
||||
import 'package:angular2/src/transform/common/ng_compiler.dart';
|
||||
import 'package:angular2/src/transform/common/ng_meta.dart';
|
||||
import 'package:barback/barback.dart' show AssetId;
|
||||
|
@ -17,26 +16,20 @@ import 'package:angular2/src/core/compiler/template_compiler.dart';
|
|||
|
||||
import 'inliner.dart';
|
||||
|
||||
/// Generates a file registering all Angular 2 `Directive`s found in `code` in
|
||||
/// ngDeps format [TODO(kegluneq): documentation reference needed]. `assetId` is
|
||||
/// the id of the asset containing `code`.
|
||||
///
|
||||
/// If no Angular 2 `Directive`s are found in `code`, returns the empty
|
||||
/// string unless `forceGenerate` is true, in which case an empty ngDeps
|
||||
/// file is created.
|
||||
Future<NgDepsModel> createNgDeps(AssetReader reader, AssetId assetId,
|
||||
AnnotationMatcher annotationMatcher, NgMeta ngMeta) async {
|
||||
/// Generates an instance of [NgMeta] describing the file at `assetId`.
|
||||
Future<NgMeta> createNgDeps(AssetReader reader, AssetId assetId,
|
||||
AnnotationMatcher annotationMatcher) async {
|
||||
// TODO(kegluneq): Shortcut if we can determine that there are no
|
||||
// [Directive]s present, taking into account `export`s.
|
||||
var codeWithParts = await inlineParts(reader, assetId);
|
||||
if (codeWithParts == null || codeWithParts.isEmpty) return null;
|
||||
|
||||
var parsedCode =
|
||||
parseCompilationUnit(codeWithParts, name: '${assetId.path} and parts');
|
||||
|
||||
var ngDepsVisitor = new NgDepsVisitor(assetId, annotationMatcher);
|
||||
parsedCode.accept(ngDepsVisitor);
|
||||
var ngDepsModel = ngDepsVisitor.model;
|
||||
|
||||
var ngMeta = new NgMeta(ngDeps: ngDepsVisitor.model);
|
||||
|
||||
var templateCompiler = createTemplateCompiler(reader);
|
||||
var ngMetaVisitor = new _NgMetaVisitor(
|
||||
|
@ -44,21 +37,9 @@ Future<NgDepsModel> createNgDeps(AssetReader reader, AssetId assetId,
|
|||
parsedCode.accept(ngMetaVisitor);
|
||||
await ngMetaVisitor.whenDone();
|
||||
|
||||
// If this file imports only dart: libraries and does not define any
|
||||
// reflectables of its own, it doesn't need a .ng_deps.dart file.
|
||||
if (ngDepsModel.reflectables == null || ngDepsModel.reflectables.isEmpty) {
|
||||
if (ngDepsModel.imports.every(_isDartImport) &&
|
||||
ngDepsModel.exports.every(_isDartImport)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return ngDepsModel;
|
||||
return ngMeta;
|
||||
}
|
||||
|
||||
// `model` can be an [ImportModel] or [ExportModel].
|
||||
bool _isDartImport(dynamic model) => model.uri.startsWith('dart:');
|
||||
|
||||
// TODO(kegluneq): Allow the caller to provide an InterfaceMatcher.
|
||||
final _interfaceMatcher = new InterfaceMatcher();
|
||||
|
||||
|
@ -96,12 +77,6 @@ class _NgMetaVisitor extends Object with SimpleAstVisitor<Object> {
|
|||
return node.declarations.accept(this);
|
||||
}
|
||||
|
||||
@override
|
||||
Object visitExportDirective(ExportDirective node) {
|
||||
ngMeta.exports.add(stringLiteralToString(node.uri));
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Object visitClassDeclaration(ClassDeclaration node) {
|
||||
_normalizations.add(_reader
|
||||
|
|
|
@ -8,7 +8,6 @@ import 'package:angular2/src/transform/common/asset_reader.dart';
|
|||
import 'package:angular2/src/transform/common/logging.dart' as log;
|
||||
import 'package:angular2/src/transform/common/names.dart';
|
||||
import 'package:angular2/src/transform/common/options.dart';
|
||||
import 'package:angular2/src/transform/common/ng_meta.dart';
|
||||
import 'package:barback/barback.dart';
|
||||
|
||||
import 'rewriter.dart';
|
||||
|
@ -37,7 +36,6 @@ class DirectiveProcessor extends Transformer implements DeclaringTransformer {
|
|||
@override
|
||||
declareOutputs(DeclaringTransform transform) {
|
||||
transform.declareOutput(_ngMetaAssetId(transform.primaryId));
|
||||
transform.declareOutput(_ngDepsAssetId(transform.primaryId));
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -46,20 +44,13 @@ class DirectiveProcessor extends Transformer implements DeclaringTransformer {
|
|||
await log.initZoned(transform, () async {
|
||||
var primaryId = transform.primaryInput.id;
|
||||
var reader = new AssetReader.fromTransform(transform);
|
||||
var ngMeta = new NgMeta.empty();
|
||||
var ngDepsModel = await createNgDeps(
|
||||
reader, primaryId, options.annotationMatcher, ngMeta);
|
||||
// TODO(kegluneq): Combine NgDepsModel with NgMeta in a single .json file.
|
||||
if (ngDepsModel != null) {
|
||||
var ngDepsAssetId = _ngDepsAssetId(primaryId);
|
||||
transform.addOutput(new Asset.fromString(
|
||||
ngDepsAssetId, _encoder.convert(ngDepsModel.writeToJsonMap())));
|
||||
}
|
||||
var metaOutputId = _ngMetaAssetId(primaryId);
|
||||
if (!ngMeta.isEmpty) {
|
||||
transform.addOutput(new Asset.fromString(
|
||||
metaOutputId, _encoder.convert(ngMeta.toJson())));
|
||||
var ngMeta =
|
||||
await createNgDeps(reader, primaryId, options.annotationMatcher);
|
||||
if (ngMeta == null || ngMeta.isEmpty) {
|
||||
return;
|
||||
}
|
||||
transform.addOutput(new Asset.fromString(
|
||||
_ngMetaAssetId(primaryId), _encoder.convert(ngMeta.toJson())));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -68,8 +59,3 @@ AssetId _ngMetaAssetId(AssetId primaryInputId) {
|
|||
return new AssetId(
|
||||
primaryInputId.package, toMetaExtension(primaryInputId.path));
|
||||
}
|
||||
|
||||
AssetId _ngDepsAssetId(AssetId primaryInputId) {
|
||||
return new AssetId(
|
||||
primaryInputId.package, toJsonExtension(primaryInputId.path));
|
||||
}
|
||||
|
|
|
@ -30,7 +30,8 @@ class CompileDataResults {
|
|||
NormalizedComponentWithViewDirectives> viewDefinitions;
|
||||
final List<CompileDirectiveMetadata> directiveMetadatas;
|
||||
|
||||
CompileDataResults._(this.ngDeps, this.viewDefinitions, this.directiveMetadatas);
|
||||
CompileDataResults._(
|
||||
this.ngDeps, this.viewDefinitions, this.directiveMetadatas);
|
||||
}
|
||||
|
||||
// TODO(kegluenq): Improve this test.
|
||||
|
@ -86,9 +87,7 @@ class _CompileDataCreator {
|
|||
Future<Map<AssetId, String>> _createImportAssetToPrefixMap() async {
|
||||
var ngDeps = await ngDepsFuture;
|
||||
|
||||
var importAssetToPrefix = <AssetId, String>{
|
||||
entryPoint: null
|
||||
};
|
||||
var importAssetToPrefix = <AssetId, String>{entryPoint: null};
|
||||
|
||||
for (ImportDirective node in ngDeps.imports) {
|
||||
var uri = stringLiteralToString(node.uri);
|
||||
|
@ -144,12 +143,15 @@ class _CompileDataCreator {
|
|||
importAssetId.package, toMetaExtension(importAssetId.path));
|
||||
if (await reader.hasInput(metaAssetId)) {
|
||||
try {
|
||||
var json = JSON.decode(await reader.readAsString(metaAssetId));
|
||||
var newMetadata = new NgMeta.fromJson(json);
|
||||
if (importAssetId == entryPoint) {
|
||||
this.directiveMetadatas.addAll(newMetadata.types.values);
|
||||
var jsonString = await reader.readAsString(metaAssetId);
|
||||
if (jsonString != null && jsonString.isNotEmpty) {
|
||||
var json = JSON.decode(jsonString);
|
||||
var newMetadata = new NgMeta.fromJson(json);
|
||||
if (importAssetId == entryPoint) {
|
||||
this.directiveMetadatas.addAll(newMetadata.types.values);
|
||||
}
|
||||
ngMeta.addAll(newMetadata);
|
||||
}
|
||||
ngMeta.addAll(newMetadata);
|
||||
} catch (ex, stackTrace) {
|
||||
logger.warning('Failed to decode: $ex, $stackTrace',
|
||||
asset: metaAssetId);
|
||||
|
|
|
@ -4,7 +4,6 @@ import 'package:barback/barback.dart';
|
|||
import 'package:dart_style/dart_style.dart';
|
||||
|
||||
import 'deferred_rewriter/transformer.dart';
|
||||
import 'directive_linker/transformer.dart';
|
||||
import 'directive_metadata_linker/transformer.dart';
|
||||
import 'directive_processor/transformer.dart';
|
||||
import 'bind_generator/transformer.dart';
|
||||
|
@ -30,14 +29,8 @@ class AngularTransformerGroup extends TransformerGroup {
|
|||
[new ReflectionRemover(options)],
|
||||
[new DirectiveProcessor(options)]
|
||||
];
|
||||
phases.addAll(new List.generate(
|
||||
options.optimizationPhases, (_) => [new EmptyNgDepsRemover()]));
|
||||
phases.addAll([
|
||||
[
|
||||
new DeferredRewriter(options),
|
||||
new DirectiveLinker(),
|
||||
new DirectiveMetadataLinker()
|
||||
],
|
||||
[new DeferredRewriter(options), new DirectiveMetadataLinker()],
|
||||
[new BindGenerator(options)],
|
||||
[new TemplateCompiler(options)],
|
||||
[new StylesheetCompiler()],
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
library angular2.test.transform.common.ng_meta_helper;
|
||||
|
||||
import 'package:angular2/src/core/compiler/directive_metadata.dart';
|
||||
import 'package:angular2/src/core/change_detection/change_detection.dart';
|
||||
import 'package:angular2/src/core/metadata/view.dart' show ViewEncapsulation;
|
||||
|
||||
export 'package:angular2/src/core/compiler/directive_metadata.dart';
|
||||
export 'package:angular2/src/core/change_detection/change_detection.dart';
|
||||
export 'package:angular2/src/core/metadata/view.dart' show ViewEncapsulation;
|
||||
export 'package:angular2/src/transform/common/model/ng_deps_model.pb.dart';
|
||||
export 'package:angular2/src/transform/common/ng_meta.dart';
|
||||
|
||||
CompileDirectiveMetadata createComponentMetadataForTest(
|
||||
{String name: 'TestMetadata',
|
||||
moduleUrl: 'asset:angular2/test/test.dart',
|
||||
selector: '[test]',
|
||||
String template: 'Test'}) {
|
||||
return createDirectiveMetadataForTest(
|
||||
name: name,
|
||||
moduleUrl: moduleUrl,
|
||||
selector: selector,
|
||||
template: new CompileTemplateMetadata(
|
||||
encapsulation: ViewEncapsulation.Emulated, template: template));
|
||||
}
|
||||
|
||||
CompileDirectiveMetadata createDirectiveMetadataForTest(
|
||||
{String name: 'TestMetadata',
|
||||
String moduleUrl: 'asset:angular2/test/test.dart',
|
||||
String selector: '[test]',
|
||||
CompileTemplateMetadata template: null}) {
|
||||
return CompileDirectiveMetadata.create(
|
||||
type: new CompileTypeMetadata(name: name, moduleUrl: moduleUrl),
|
||||
isComponent: false,
|
||||
dynamicLoadable: true,
|
||||
selector: selector,
|
||||
exportAs: null,
|
||||
changeDetection: ChangeDetectionStrategy.Default,
|
||||
inputs: [],
|
||||
outputs: [],
|
||||
host: {},
|
||||
lifecycleHooks: [],
|
||||
template: template);
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
library angular2.test.transform.common.url_resolver_tests;
|
||||
|
||||
import 'package:angular2/src/transform/common/url_resolver.dart';
|
||||
import 'package:barback/barback.dart';
|
||||
import 'package:guinness/guinness.dart';
|
||||
|
||||
main() => allTests();
|
||||
|
||||
void allTests() {
|
||||
var urlResolver = const TransformerUrlResolver();
|
||||
|
||||
describe('toAssetUri', () {
|
||||
it('should convert `AssetId`s to asset: uris', () {
|
||||
var assetId = new AssetId('test_package', 'lib/src/impl.dart');
|
||||
expect(toAssetUri(assetId))
|
||||
.toEqual('asset:test_package/lib/src/impl.dart');
|
||||
});
|
||||
|
||||
it('should throw if passed a null AssetId', () {
|
||||
expect(() => toAssetUri(null)).toThrowWith(anInstanceOf: ArgumentError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fromUri', () {
|
||||
it('should convert asset: `uri`s to `AssetId`s', () {
|
||||
expect(fromUri('asset:test_package/lib/src/impl.dart'))
|
||||
.toEqual(new AssetId('test_package', 'lib/src/impl.dart'));
|
||||
});
|
||||
|
||||
it('should convert package: `uri`s to `AssetId`s', () {
|
||||
expect(fromUri('package:test_package/src/impl.dart'))
|
||||
.toEqual(new AssetId('test_package', 'lib/src/impl.dart'));
|
||||
});
|
||||
|
||||
it('should throw if passed a null uri', () {
|
||||
expect(() => fromUri(null)).toThrowWith(anInstanceOf: ArgumentError);
|
||||
});
|
||||
|
||||
it('should throw if passed an empty uri', () {
|
||||
expect(() => fromUri('')).toThrowWith(anInstanceOf: ArgumentError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isDartCoreUri', () {
|
||||
it('should detect dart: uris', () {
|
||||
expect(isDartCoreUri('dart:core')).toBeTrue();
|
||||
expect(isDartCoreUri('dart:convert')).toBeTrue();
|
||||
expect(isDartCoreUri('package:angular2/angular2.dart')).toBeFalse();
|
||||
expect(isDartCoreUri('asset:angular2/lib/angular2.dart')).toBeFalse();
|
||||
});
|
||||
|
||||
it('should throw if passed a null uri', () {
|
||||
expect(() => isDartCoreUri(null))
|
||||
.toThrowWith(anInstanceOf: ArgumentError);
|
||||
});
|
||||
|
||||
it('should throw if passed an empty uri', () {
|
||||
expect(() => isDartCoreUri('')).toThrowWith(anInstanceOf: ArgumentError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toAssetScheme', () {
|
||||
it('should throw for relative `Uri`s', () {
|
||||
expect(() => toAssetScheme(Uri.parse('/lib/src/file.dart')))
|
||||
.toThrowWith(anInstanceOf: ArgumentError);
|
||||
});
|
||||
|
||||
it('should convert package: `Uri`s to asset:', () {
|
||||
expect(toAssetScheme(Uri.parse('package:angular2/angular2.dart')))
|
||||
.toEqual(Uri.parse('asset:angular2/lib/angular2.dart'));
|
||||
});
|
||||
|
||||
it('should throw for package: `Uri`s which are too short', () {
|
||||
expect(() => toAssetScheme(Uri.parse('package:angular2')))
|
||||
.toThrowWith(anInstanceOf: FormatException);
|
||||
});
|
||||
|
||||
it('should convert asset: `Uri`s to asset:', () {
|
||||
expect(toAssetScheme(Uri.parse('asset:angular2/lib/angular2.dart')))
|
||||
.toEqual(Uri.parse('asset:angular2/lib/angular2.dart'));
|
||||
});
|
||||
|
||||
it('should throw for asset: `Uri`s which are too short', () {
|
||||
expect(() => toAssetScheme(Uri.parse('asset:angular2')))
|
||||
.toThrowWith(anInstanceOf: FormatException);
|
||||
|
||||
expect(() => toAssetScheme(Uri.parse('asset:angular2/lib')))
|
||||
.toThrowWith(anInstanceOf: FormatException);
|
||||
});
|
||||
|
||||
it('should throw for unsupported schemes', () {
|
||||
expect(() => toAssetScheme(Uri.parse('file:///angular2')))
|
||||
.toThrowWith(anInstanceOf: FormatException);
|
||||
});
|
||||
|
||||
it('should throw if passed a null uri', () {
|
||||
expect(() => toAssetScheme(null))
|
||||
.toThrowWith(anInstanceOf: ArgumentError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolve', () {
|
||||
it('should resolve package: uris to asset: uris', () {
|
||||
expect(urlResolver.resolve('', 'package:angular2/angular2.dart'))
|
||||
.toEqual('asset:angular2/lib/angular2.dart');
|
||||
});
|
||||
|
||||
it('should ignore baseUrl for absolute uris', () {
|
||||
expect(urlResolver.resolve(null, 'package:angular2/angular2.dart'))
|
||||
.toEqual('asset:angular2/lib/angular2.dart');
|
||||
expect(urlResolver.resolve(null, 'asset:angular2/lib/angular2.dart'))
|
||||
.toEqual('asset:angular2/lib/angular2.dart');
|
||||
});
|
||||
|
||||
it('should resolve asset: uris to asset: uris', () {
|
||||
expect(urlResolver.resolve('', 'asset:angular2/lib/angular2.dart'))
|
||||
.toEqual('asset:angular2/lib/angular2.dart');
|
||||
});
|
||||
|
||||
it('should resolve relative uris when baseUrl is package: uri', () {
|
||||
expect(urlResolver.resolve('package:angular2/angular2.dart',
|
||||
'src/transform/transformer.dart'))
|
||||
.toEqual('asset:angular2/lib/src/transform/transformer.dart');
|
||||
});
|
||||
|
||||
it('should resolve relative uris when baseUrl is asset: uri', () {
|
||||
expect(urlResolver.resolve('asset:angular2/lib/angular2.dart',
|
||||
'src/transform/transformer.dart'))
|
||||
.toEqual('asset:angular2/lib/src/transform/transformer.dart');
|
||||
});
|
||||
|
||||
it('should normalize uris', () {
|
||||
expect(urlResolver.resolve('asset:angular2/lib/angular2.dart',
|
||||
'src/transform/../transform/transformer.dart'))
|
||||
.toEqual('asset:angular2/lib/src/transform/transformer.dart');
|
||||
expect(urlResolver.resolve('asset:angular2/lib/src/../angular2.dart',
|
||||
'src/transform/transformer.dart'))
|
||||
.toEqual('asset:angular2/lib/src/transform/transformer.dart');
|
||||
});
|
||||
|
||||
it('should throw if passed a null uri', () {
|
||||
expect(() => urlResolver.resolve('package:angular2/angular2.dart', null))
|
||||
.toThrowWith(anInstanceOf: ArgumentError);
|
||||
});
|
||||
|
||||
it('should gracefully handle an empty uri', () {
|
||||
expect(urlResolver.resolve('package:angular2/angular2.dart', ''))
|
||||
.toEqual('asset:angular2/lib/angular2.dart');
|
||||
});
|
||||
|
||||
it('should throw if passed a relative uri and a null baseUri', () {
|
||||
expect(() => urlResolver.resolve(null, 'angular2/angular2.dart'))
|
||||
.toThrowWith(anInstanceOf: ArgumentError);
|
||||
});
|
||||
|
||||
it('should throw if passed a relative uri and an empty baseUri', () {
|
||||
expect(() => urlResolver.resolve('', 'angular2/angular2.dart'))
|
||||
.toThrowWith(anInstanceOf: ArgumentError);
|
||||
});
|
||||
|
||||
it('should throw if the resolved uri is relative', () {
|
||||
expect(() => urlResolver.resolve('/angular2/', 'angular2.dart'))
|
||||
.toThrowWith(anInstanceOf: ArgumentError);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,137 +0,0 @@
|
|||
library angular2.test.transform.directive_linker.all_tests;
|
||||
|
||||
import 'package:barback/barback.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/model/reflection_info_model.pb.dart';
|
||||
import 'package:angular2/src/transform/directive_linker/linker.dart';
|
||||
import 'package:dart_style/dart_style.dart';
|
||||
import 'package:guinness/guinness.dart';
|
||||
|
||||
import '../common/read_file.dart';
|
||||
|
||||
var formatter = new DartFormatter();
|
||||
|
||||
main() => allTests();
|
||||
|
||||
void allTests() {
|
||||
var reader;
|
||||
|
||||
beforeEach(() {
|
||||
reader = new TestAssetReader();
|
||||
});
|
||||
|
||||
it('should chain imported dependencies.', () async {
|
||||
var fooModel = new NgDepsModel()
|
||||
..libraryUri = 'test.foo'
|
||||
..imports.add(new ImportModel()
|
||||
..uri = 'bar.dart'
|
||||
..prefix = 'dep');
|
||||
var barModel = new NgDepsModel()..libraryUri = 'test.bar';
|
||||
|
||||
var fooAssetId = new AssetId('a', 'lib/foo.ng_deps.json');
|
||||
reader
|
||||
..addAsset(fooAssetId, fooModel.writeToJson())
|
||||
..addAsset(
|
||||
new AssetId('a', 'lib/bar.ng_deps.json'), barModel.writeToJson());
|
||||
|
||||
var linked = await linkNgDeps(reader, fooAssetId);
|
||||
expect(linked).toBeNotNull();
|
||||
var linkedImport =
|
||||
linked.imports.firstWhere((i) => i.uri.endsWith('bar.ng_deps.dart'));
|
||||
expect(linkedImport).toBeNotNull();
|
||||
expect(linkedImport.isNgDeps).toBeTrue();
|
||||
expect(linkedImport.prefix.startsWith('i')).toBeTrue();
|
||||
});
|
||||
|
||||
it('should chain exported dependencies.', () async {
|
||||
var fooModel = new NgDepsModel()
|
||||
..libraryUri = 'test.foo'
|
||||
..exports.add(new ExportModel()..uri = 'bar.dart');
|
||||
var barModel = new NgDepsModel()..libraryUri = 'test.bar';
|
||||
|
||||
var fooAssetId = new AssetId('a', 'lib/foo.ng_deps.json');
|
||||
reader
|
||||
..addAsset(fooAssetId, fooModel.writeToJson())
|
||||
..addAsset(
|
||||
new AssetId('a', 'lib/bar.ng_deps.json'), barModel.writeToJson());
|
||||
|
||||
var linked = await linkNgDeps(reader, fooAssetId);
|
||||
expect(linked).toBeNotNull();
|
||||
var linkedImport =
|
||||
linked.imports.firstWhere((i) => i.uri.endsWith('bar.ng_deps.dart'));
|
||||
expect(linkedImport).toBeNotNull();
|
||||
expect(linkedImport.isNgDeps).toBeTrue();
|
||||
expect(linkedImport.prefix.startsWith('i')).toBeTrue();
|
||||
});
|
||||
|
||||
describe('isNecessary', () {
|
||||
it('should drop deps that do no registration and do not import.', () async {
|
||||
var fooModel = new NgDepsModel()..libraryUri = 'test.foo';
|
||||
|
||||
var fooAssetId = new AssetId('a', 'lib/foo.ng_deps.json');
|
||||
reader.addAsset(fooAssetId, fooModel.writeToJson());
|
||||
expect(await isNecessary(reader, fooAssetId)).toBeFalse();
|
||||
});
|
||||
|
||||
it('should retain deps that import other deps.', () async {
|
||||
var fooModel = new NgDepsModel()
|
||||
..libraryUri = 'test.foo'
|
||||
..imports.add(new ImportModel()..uri = 'bar.dart');
|
||||
var barModel = new NgDepsModel()..libraryUri = 'test.bar';
|
||||
|
||||
var fooAssetId = new AssetId('a', 'lib/foo.ng_deps.json');
|
||||
reader
|
||||
..addAsset(fooAssetId, fooModel.writeToJson())
|
||||
..addAsset(
|
||||
new AssetId('a', 'lib/bar.ng_deps.json'), barModel.writeToJson());
|
||||
|
||||
expect(await isNecessary(reader, fooAssetId)).toBeTrue();
|
||||
});
|
||||
|
||||
it('should retain deps that export other deps.', () async {
|
||||
var fooModel = new NgDepsModel()
|
||||
..libraryUri = 'test.foo'
|
||||
..exports.add(new ExportModel()..uri = 'bar.dart');
|
||||
var barModel = new NgDepsModel()..libraryUri = 'test.bar';
|
||||
|
||||
var fooAssetId = new AssetId('a', 'lib/foo.ng_deps.json');
|
||||
reader
|
||||
..addAsset(fooAssetId, fooModel.writeToJson())
|
||||
..addAsset(
|
||||
new AssetId('a', 'lib/bar.ng_deps.json'), barModel.writeToJson());
|
||||
|
||||
expect(await isNecessary(reader, fooAssetId)).toBeTrue();
|
||||
});
|
||||
|
||||
it('should retain deps that register injectable types.', () async {
|
||||
var fooModel = new NgDepsModel()
|
||||
..libraryUri = 'test.foo'
|
||||
..reflectables.add(new ReflectionInfoModel()
|
||||
..name = 'MyInjectable'
|
||||
..annotations.add(new AnnotationModel()
|
||||
..name = 'Injectable'
|
||||
..isInjectable = true));
|
||||
|
||||
var fooAssetId = new AssetId('a', 'lib/foo.ng_deps.json');
|
||||
reader.addAsset(fooAssetId, fooModel.writeToJson());
|
||||
expect(await isNecessary(reader, fooAssetId)).toBeTrue();
|
||||
});
|
||||
|
||||
it('should retain deps that register injectable functions.', () async {
|
||||
var fooModel = new NgDepsModel()
|
||||
..libraryUri = 'test.foo'
|
||||
..reflectables.add(new ReflectionInfoModel()
|
||||
..name = 'injectableFunction'
|
||||
..isFunction = true
|
||||
..annotations.add(new AnnotationModel()
|
||||
..name = 'Injectable'
|
||||
..isInjectable = true));
|
||||
|
||||
var fooAssetId = new AssetId('a', 'lib/foo.ng_deps.json');
|
||||
reader.addAsset(fooAssetId, fooModel.writeToJson());
|
||||
expect(await isNecessary(reader, fooAssetId)).toBeTrue();
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
library foo.ng_deps.dart;
|
||||
|
||||
import 'bar.dart';
|
||||
import 'package:angular2/src/core/metadata.dart';
|
||||
|
||||
export 'baz.dart';
|
||||
import 'baz.ng_deps.dart' as i0;
|
||||
|
||||
var _visited = false;
|
||||
void initReflector(reflector) {
|
||||
if (_visited) return;
|
||||
_visited = true;
|
||||
reflector
|
||||
..registerType(
|
||||
BarComponent,
|
||||
new ReflectionInfo(const [const Component(selector: '[bar]')], const [],
|
||||
() => new BarComponent()));
|
||||
i0.initReflector(reflector);
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
library foo.ng_deps.dart;
|
||||
|
||||
import 'baz.dart';
|
||||
import 'package:angular2/src/core/metadata.dart';
|
||||
|
||||
export 'foo.dart';
|
||||
|
||||
var _visited = false;
|
||||
void initReflector(reflector) {
|
||||
if (_visited) return;
|
||||
_visited = true;
|
||||
reflector
|
||||
..registerType(
|
||||
BazComponent,
|
||||
new ReflectionInfo(const [const Component(selector: '[baz]')], const [],
|
||||
() => new BazComponent()));
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
library foo.ng_deps.dart;
|
||||
|
||||
import 'foo.dart';
|
||||
import 'package:angular2/src/core/metadata.dart';
|
||||
|
||||
export 'bar.dart';
|
||||
import 'bar.ng_deps.dart' as i0;
|
||||
|
||||
var _visited = false;
|
||||
void initReflector(reflector) {
|
||||
if (_visited) return;
|
||||
_visited = true;
|
||||
reflector
|
||||
..registerType(
|
||||
FooComponent,
|
||||
new ReflectionInfo(const [const Component(selector: '[foo]')], const [],
|
||||
() => new FooComponent()));
|
||||
i0.initReflector(reflector);
|
||||
}
|
|
@ -1,17 +1,14 @@
|
|||
library angular2.test.transform.directive_metadata_linker.all_tests;
|
||||
|
||||
import 'dart:async';
|
||||
import 'package:angular2/src/core/render/api.dart';
|
||||
import 'package:angular2/src/core/change_detection/change_detection.dart';
|
||||
import 'package:angular2/src/transform/common/directive_metadata_reader.dart';
|
||||
import 'package:angular2/src/transform/common/logging.dart';
|
||||
import 'package:angular2/src/transform/common/ng_deps.dart';
|
||||
import 'package:angular2/src/transform/directive_metadata_linker/'
|
||||
'linker.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:angular2/src/transform/common/model/import_export_model.pb.dart';
|
||||
import 'package:angular2/src/transform/directive_metadata_linker/ng_meta_linker.dart';
|
||||
import 'package:barback/barback.dart';
|
||||
import 'package:dart_style/dart_style.dart';
|
||||
import 'package:guinness/guinness.dart';
|
||||
|
||||
import '../common/ng_meta_helper.dart';
|
||||
import '../common/read_file.dart';
|
||||
|
||||
var formatter = new DartFormatter();
|
||||
|
@ -20,59 +17,148 @@ main() => allTests();
|
|||
|
||||
void allTests() {
|
||||
TestAssetReader reader = null;
|
||||
final moduleBase = 'asset:a';
|
||||
var fooNgMeta, fooAssetId;
|
||||
var barNgMeta, barAssetId;
|
||||
var bazNgMeta, bazAssetId;
|
||||
|
||||
/// Call after making changes to `fooNgMeta`, `barNgMeta`, or `bazNgMeta` and
|
||||
/// before trying to read them from `reader`.
|
||||
final updateReader = () => reader
|
||||
..addAsset(fooAssetId, JSON.encode(fooNgMeta.toJson()))
|
||||
..addAsset(barAssetId, JSON.encode(barNgMeta.toJson()))
|
||||
..addAsset(bazAssetId, JSON.encode(bazNgMeta.toJson()));
|
||||
|
||||
beforeEach(() {
|
||||
reader = new TestAssetReader();
|
||||
|
||||
// Establish some test NgMeta objects with one Component each.
|
||||
var fooName = 'FooComponent';
|
||||
var fooComponentMeta = createComponentMetadataForTest(
|
||||
name: fooName,
|
||||
moduleUrl: '$moduleBase/export_cycle_files/foo.dart',
|
||||
selector: '[foo]',
|
||||
template: 'Foo');
|
||||
fooNgMeta = new NgMeta(ngDeps: new NgDepsModel());
|
||||
fooNgMeta.types[fooName] = fooComponentMeta;
|
||||
|
||||
var barName = 'BarComponent';
|
||||
var barComponentMeta = createComponentMetadataForTest(
|
||||
name: barName,
|
||||
moduleUrl: '$moduleBase/export_cycle_files/bar.dart',
|
||||
selector: '[bar]',
|
||||
template: 'Bar');
|
||||
barNgMeta = new NgMeta(ngDeps: new NgDepsModel());
|
||||
barNgMeta.types[barName] = barComponentMeta;
|
||||
|
||||
var bazName = 'BazComponent';
|
||||
var bazComponentMeta = createComponentMetadataForTest(
|
||||
name: bazName,
|
||||
moduleUrl: '$moduleBase/export_cycle_files/baz.dart',
|
||||
selector: '[baz]',
|
||||
template: 'Baz');
|
||||
bazNgMeta = new NgMeta(ngDeps: new NgDepsModel());
|
||||
barNgMeta.types[bazName] = bazComponentMeta;
|
||||
|
||||
fooAssetId = new AssetId('a', 'lib/foo.ng_meta.json');
|
||||
barAssetId = new AssetId('a', 'lib/bar.ng_meta.json');
|
||||
bazAssetId = new AssetId('a', 'lib/baz.ng_meta.json');
|
||||
updateReader();
|
||||
});
|
||||
|
||||
it('should include `DirectiveMetadata` from exported files.', () async {
|
||||
var extracted = await linkDirectiveMetadata(
|
||||
reader,
|
||||
new AssetId(
|
||||
'a', 'directive_metadata_linker/export_files/foo.ng_meta.json'));
|
||||
expect(extracted.types).toContain('FooComponent');
|
||||
expect(extracted.types).toContain('BarComponent');
|
||||
describe('NgMeta linker', () {
|
||||
it('should include `DirectiveMetadata` from exported files.', () async {
|
||||
fooNgMeta.ngDeps.exports.add(new ExportModel()..uri = 'bar.dart');
|
||||
updateReader();
|
||||
|
||||
expect(extracted.types['FooComponent'].selector).toEqual('[foo]');
|
||||
expect(extracted.types['BarComponent'].selector).toEqual('[bar]');
|
||||
var extracted = await linkDirectiveMetadata(reader, fooAssetId);
|
||||
expect(extracted.types).toContain('FooComponent');
|
||||
expect(extracted.types).toContain('BarComponent');
|
||||
|
||||
expect(extracted.types['FooComponent'].selector).toEqual('[foo]');
|
||||
expect(extracted.types['BarComponent'].selector).toEqual('[bar]');
|
||||
});
|
||||
|
||||
it('should include `DirectiveMetadata` recursively from exported files.',
|
||||
() async {
|
||||
fooNgMeta.ngDeps.exports.add(new ExportModel()..uri = 'bar.dart');
|
||||
barNgMeta.ngDeps.exports.add(new ExportModel()..uri = 'baz.dart');
|
||||
updateReader();
|
||||
|
||||
var extracted = await linkDirectiveMetadata(reader, fooAssetId);
|
||||
expect(extracted.types).toContain('FooComponent');
|
||||
expect(extracted.types).toContain('BarComponent');
|
||||
expect(extracted.types).toContain('BazComponent');
|
||||
|
||||
expect(extracted.types['FooComponent'].selector).toEqual('[foo]');
|
||||
expect(extracted.types['BarComponent'].selector).toEqual('[bar]');
|
||||
expect(extracted.types['BazComponent'].selector).toEqual('[baz]');
|
||||
});
|
||||
|
||||
it('should handle `DirectiveMetadata` export cycles gracefully.', () async {
|
||||
fooNgMeta.ngDeps.exports.add(new ExportModel()..uri = 'bar.dart');
|
||||
barNgMeta.ngDeps.exports.add(new ExportModel()..uri = 'baz.dart');
|
||||
bazNgMeta.ngDeps.exports.add(new ExportModel()..uri = 'foo.dart');
|
||||
updateReader();
|
||||
|
||||
var extracted = await linkDirectiveMetadata(reader, bazAssetId);
|
||||
expect(extracted.types).toContain('FooComponent');
|
||||
expect(extracted.types).toContain('BarComponent');
|
||||
expect(extracted.types).toContain('BazComponent');
|
||||
});
|
||||
|
||||
it(
|
||||
'should include `DirectiveMetadata` from exported files '
|
||||
'expressed as absolute uris', () async {
|
||||
fooNgMeta.ngDeps.exports
|
||||
.add(new ExportModel()..uri = 'package:bar/bar.dart');
|
||||
updateReader();
|
||||
reader.addAsset(new AssetId('bar', 'lib/bar.ng_meta.json'),
|
||||
JSON.encode(barNgMeta.toJson()));
|
||||
|
||||
var extracted = await linkDirectiveMetadata(reader, fooAssetId);
|
||||
|
||||
expect(extracted.types).toContain('FooComponent');
|
||||
expect(extracted.types).toContain('BarComponent');
|
||||
|
||||
expect(extracted.types['FooComponent'].selector).toEqual('[foo]');
|
||||
expect(extracted.types['BarComponent'].selector).toEqual('[bar]');
|
||||
});
|
||||
});
|
||||
|
||||
it('should include `DirectiveMetadata` recursively from exported files.',
|
||||
() async {
|
||||
var extracted = await linkDirectiveMetadata(
|
||||
reader,
|
||||
new AssetId('a',
|
||||
'directive_metadata_linker/recursive_export_files/foo.ng_meta.json'));
|
||||
expect(extracted.types).toContain('FooComponent');
|
||||
expect(extracted.types).toContain('BarComponent');
|
||||
expect(extracted.types).toContain('BazComponent');
|
||||
describe('NgDeps linker', () {
|
||||
it('should chain imported dependencies.', () async {
|
||||
fooNgMeta.ngDeps
|
||||
..libraryUri = 'test.foo'
|
||||
..imports.add(new ImportModel()
|
||||
..uri = 'bar.dart'
|
||||
..prefix = 'dep');
|
||||
barNgMeta.ngDeps.libraryUri = 'test.bar';
|
||||
updateReader();
|
||||
|
||||
expect(extracted.types['FooComponent'].selector).toEqual('[foo]');
|
||||
expect(extracted.types['BarComponent'].selector).toEqual('[bar]');
|
||||
expect(extracted.types['BazComponent'].selector).toEqual('[baz]');
|
||||
});
|
||||
var linked = (await linkDirectiveMetadata(reader, fooAssetId)).ngDeps;
|
||||
expect(linked).toBeNotNull();
|
||||
var linkedImport =
|
||||
linked.imports.firstWhere((i) => i.uri.endsWith('bar.ng_deps.dart'));
|
||||
expect(linkedImport).toBeNotNull();
|
||||
expect(linkedImport.isNgDeps).toBeTrue();
|
||||
expect(linkedImport.prefix.startsWith('i')).toBeTrue();
|
||||
});
|
||||
|
||||
it('should handle `DirectiveMetadata` export cycles gracefully.', () async {
|
||||
var extracted = await linkDirectiveMetadata(
|
||||
reader,
|
||||
new AssetId('a',
|
||||
'directive_metadata_linker/export_cycle_files/baz.ng_meta.json'));
|
||||
expect(extracted.types).toContain('FooComponent');
|
||||
expect(extracted.types).toContain('BarComponent');
|
||||
expect(extracted.types).toContain('BazComponent');
|
||||
});
|
||||
it('should chain exported dependencies.', () async {
|
||||
fooNgMeta.ngDeps
|
||||
..libraryUri = 'test.foo'
|
||||
..exports.add(new ExportModel()..uri = 'bar.dart');
|
||||
barNgMeta.ngDeps.libraryUri = 'test.bar';
|
||||
updateReader();
|
||||
|
||||
it(
|
||||
'should include `DirectiveMetadata` from exported files '
|
||||
'expressed as absolute uris', () async {
|
||||
var extracted = await linkDirectiveMetadata(
|
||||
reader,
|
||||
new AssetId('a',
|
||||
'directive_metadata_linker/absolute_export_files/foo.ng_meta.json'));
|
||||
expect(extracted.types).toContain('FooComponent');
|
||||
expect(extracted.types).toContain('BarComponent');
|
||||
|
||||
expect(extracted.types['FooComponent'].selector).toEqual('[foo]');
|
||||
expect(extracted.types['BarComponent'].selector).toEqual('[bar]');
|
||||
var linked = (await linkDirectiveMetadata(reader, fooAssetId)).ngDeps;
|
||||
expect(linked).toBeNotNull();
|
||||
var linkedImport =
|
||||
linked.imports.firstWhere((i) => i.uri.endsWith('bar.ng_deps.dart'));
|
||||
expect(linkedImport).toBeNotNull();
|
||||
expect(linkedImport.isNgDeps).toBeTrue();
|
||||
expect(linkedImport.prefix.startsWith('i')).toBeTrue();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ Expect _expectSelector(ReflectionInfoModel model) {
|
|||
|
||||
void allTests() {
|
||||
it('should preserve parameter annotations.', () async {
|
||||
var model = await _testCreateModel('parameter_metadata/soup.dart');
|
||||
var model = (await _testCreateModel('parameter_metadata/soup.dart')).ngDeps;
|
||||
expect(model.reflectables.length).toBe(1);
|
||||
var reflectable = model.reflectables.first;
|
||||
expect(reflectable.parameters.length).toBe(2);
|
||||
|
@ -59,7 +59,8 @@ void allTests() {
|
|||
});
|
||||
|
||||
describe('part support', () {
|
||||
var modelFuture = _testCreateModel('part_files/main.dart');
|
||||
var modelFuture = _testCreateModel('part_files/main.dart')
|
||||
.then((ngMeta) => ngMeta != null ? ngMeta.ngDeps : null);
|
||||
|
||||
it('should include directives from the part.', () async {
|
||||
var model = await modelFuture;
|
||||
|
@ -79,56 +80,59 @@ void allTests() {
|
|||
});
|
||||
|
||||
it('should handle multiple `part` directives.', () async {
|
||||
var model = await _testCreateModel('multiple_part_files/main.dart');
|
||||
var model =
|
||||
(await _testCreateModel('multiple_part_files/main.dart')).ngDeps;
|
||||
expect(model.reflectables.length).toEqual(3);
|
||||
_expectSelector(model.reflectables.first).toEqual("'[part1]'");
|
||||
_expectSelector(model.reflectables[1]).toEqual("'[part2]'");
|
||||
_expectSelector(model.reflectables[2]).toEqual("'[main]'");
|
||||
});
|
||||
|
||||
it('should not generate .ng_deps.dart for `part` files.', () async {
|
||||
var model = await _testCreateModel('part_files/part.dart');
|
||||
expect(model).toBeNull();
|
||||
it('should not generate anything for `part` files.', () async {
|
||||
expect(await _testCreateModel('part_files/part.dart')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('custom annotations', () {
|
||||
it('should be recognized from package: imports', () async {
|
||||
var model =
|
||||
var ngMeta =
|
||||
await _testCreateModel('custom_metadata/package_soup.dart', customDescriptors:
|
||||
[
|
||||
const ClassDescriptor('Soup', 'package:soup/soup.dart',
|
||||
superClass: 'Component')
|
||||
]);
|
||||
var model = ngMeta.ngDeps;
|
||||
expect(model.reflectables.length).toEqual(1);
|
||||
expect(model.reflectables.first.name).toEqual('PackageSoup');
|
||||
});
|
||||
|
||||
it('should be recognized from relative imports', () async {
|
||||
var model = await _testCreateModel('custom_metadata/relative_soup.dart',
|
||||
var ngMeta = await _testCreateModel('custom_metadata/relative_soup.dart',
|
||||
assetId: new AssetId('soup', 'lib/relative_soup.dart'),
|
||||
customDescriptors: [
|
||||
const ClassDescriptor('Soup', 'package:soup/annotations/soup.dart',
|
||||
superClass: 'Component')
|
||||
]);
|
||||
var model = ngMeta.ngDeps;
|
||||
expect(model.reflectables.length).toEqual(1);
|
||||
expect(model.reflectables.first.name).toEqual('RelativeSoup');
|
||||
});
|
||||
|
||||
it('should ignore annotations that are not imported', () async {
|
||||
var model =
|
||||
var ngMeta =
|
||||
await _testCreateModel('custom_metadata/bad_soup.dart', customDescriptors:
|
||||
[
|
||||
const ClassDescriptor('Soup', 'package:soup/soup.dart',
|
||||
superClass: 'Component')
|
||||
]);
|
||||
expect(model).toBeNull();
|
||||
expect(ngMeta.ngDeps == null || ngMeta.ngDeps.reflectables.isEmpty)
|
||||
.toBeTrue();
|
||||
});
|
||||
});
|
||||
|
||||
describe('interfaces', () {
|
||||
it('should include implemented types', () async {
|
||||
var model = await _testCreateModel('interfaces_files/soup.dart');
|
||||
var model = (await _testCreateModel('interfaces_files/soup.dart')).ngDeps;
|
||||
|
||||
expect(model.reflectables.first.interfaces).toBeNotNull();
|
||||
expect(model.reflectables.first.interfaces.isNotEmpty).toBeTrue();
|
||||
|
@ -139,7 +143,8 @@ void allTests() {
|
|||
});
|
||||
|
||||
it('should not include transitively implemented types', () async {
|
||||
var model = await _testCreateModel('interface_chain_files/soup.dart');
|
||||
var model =
|
||||
(await _testCreateModel('interface_chain_files/soup.dart')).ngDeps;
|
||||
|
||||
expect(model.reflectables.first.interfaces).toBeNotNull();
|
||||
expect(model.reflectables.first.interfaces.isNotEmpty).toBeTrue();
|
||||
|
@ -152,15 +157,15 @@ void allTests() {
|
|||
});
|
||||
|
||||
it('should not include superclasses.', () async {
|
||||
var model = await _testCreateModel('superclass_files/soup.dart');
|
||||
var model = (await _testCreateModel('superclass_files/soup.dart')).ngDeps;
|
||||
|
||||
var interfaces = model.reflectables.first.interfaces;
|
||||
expect(interfaces == null || interfaces.isEmpty).toBeTrue();
|
||||
});
|
||||
|
||||
it('should populate multiple `lifecycle` values when necessary.', () async {
|
||||
var model = await _testCreateModel(
|
||||
'multiple_interface_lifecycle_files/soup.dart');
|
||||
var model = (await _testCreateModel(
|
||||
'multiple_interface_lifecycle_files/soup.dart')).ngDeps;
|
||||
|
||||
expect(model.reflectables.first.interfaces).toBeNotNull();
|
||||
expect(model.reflectables.first.interfaces.isNotEmpty).toBeTrue();
|
||||
|
@ -174,15 +179,16 @@ void allTests() {
|
|||
it('should not populate `lifecycle` when lifecycle superclass is present.',
|
||||
() async {
|
||||
var model =
|
||||
await _testCreateModel('superclass_lifecycle_files/soup.dart');
|
||||
(await _testCreateModel('superclass_lifecycle_files/soup.dart'))
|
||||
.ngDeps;
|
||||
|
||||
var interfaces = model.reflectables.first.interfaces;
|
||||
expect(interfaces == null || interfaces.isEmpty).toBeTrue();
|
||||
});
|
||||
|
||||
it('should populate `lifecycle` with prefix when necessary.', () async {
|
||||
var model = await _testCreateModel(
|
||||
'prefixed_interface_lifecycle_files/soup.dart');
|
||||
var model = (await _testCreateModel(
|
||||
'prefixed_interface_lifecycle_files/soup.dart')).ngDeps;
|
||||
expect(model.reflectables.first.interfaces).toBeNotNull();
|
||||
expect(model.reflectables.first.interfaces.isNotEmpty).toBeTrue();
|
||||
expect(model.reflectables.first.interfaces
|
||||
|
@ -193,7 +199,8 @@ void allTests() {
|
|||
|
||||
describe('property metadata', () {
|
||||
it('should be recorded on fields', () async {
|
||||
var model = await _testCreateModel('prop_metadata_files/fields.dart');
|
||||
var model =
|
||||
(await _testCreateModel('prop_metadata_files/fields.dart')).ngDeps;
|
||||
|
||||
expect(model.reflectables.first.propertyMetadata).toBeNotNull();
|
||||
expect(model.reflectables.first.propertyMetadata.isNotEmpty).toBeTrue();
|
||||
|
@ -205,7 +212,8 @@ void allTests() {
|
|||
});
|
||||
|
||||
it('should be recorded on getters', () async {
|
||||
var model = await _testCreateModel('prop_metadata_files/getters.dart');
|
||||
var model =
|
||||
(await _testCreateModel('prop_metadata_files/getters.dart')).ngDeps;
|
||||
|
||||
expect(model.reflectables.first.propertyMetadata).toBeNotNull();
|
||||
expect(model.reflectables.first.propertyMetadata.isNotEmpty).toBeTrue();
|
||||
|
@ -221,7 +229,8 @@ void allTests() {
|
|||
|
||||
it('should gracefully handle const instances of annotations', () async {
|
||||
// Regression test for i/4481
|
||||
var model = await _testCreateModel('prop_metadata_files/override.dart');
|
||||
var model =
|
||||
(await _testCreateModel('prop_metadata_files/override.dart')).ngDeps;
|
||||
|
||||
expect(model.reflectables.first.propertyMetadata).toBeNotNull();
|
||||
expect(model.reflectables.first.propertyMetadata.isNotEmpty).toBeTrue();
|
||||
|
@ -240,7 +249,8 @@ void allTests() {
|
|||
});
|
||||
|
||||
it('should be recorded on setters', () async {
|
||||
var model = await _testCreateModel('prop_metadata_files/setters.dart');
|
||||
var model =
|
||||
(await _testCreateModel('prop_metadata_files/setters.dart')).ngDeps;
|
||||
|
||||
expect(model.reflectables.first.propertyMetadata).toBeNotNull();
|
||||
expect(model.reflectables.first.propertyMetadata.isNotEmpty).toBeTrue();
|
||||
|
@ -253,8 +263,8 @@ void allTests() {
|
|||
|
||||
it('should be coalesced when getters and setters have the same name',
|
||||
() async {
|
||||
var model = await _testCreateModel(
|
||||
'prop_metadata_files/getters_and_setters.dart');
|
||||
var model = (await _testCreateModel(
|
||||
'prop_metadata_files/getters_and_setters.dart')).ngDeps;
|
||||
|
||||
expect(model.reflectables.first.propertyMetadata).toBeNotNull();
|
||||
expect(model.reflectables.first.propertyMetadata.length).toBe(1);
|
||||
|
@ -271,8 +281,7 @@ void allTests() {
|
|||
|
||||
it('should not throw/hang on invalid urls', () async {
|
||||
var logger = new RecordingLogger();
|
||||
var model =
|
||||
await _testCreateModel('invalid_url_files/hello.dart', logger: logger);
|
||||
await _testCreateModel('invalid_url_files/hello.dart', logger: logger);
|
||||
expect(logger.hasErrors).toBeTrue();
|
||||
expect(logger.logs)
|
||||
..toContain('ERROR: ERROR: Invalid argument (url): '
|
||||
|
@ -280,7 +289,8 @@ void allTests() {
|
|||
});
|
||||
|
||||
it('should find and register static functions.', () async {
|
||||
var model = await _testCreateModel('static_function_files/hello.dart');
|
||||
var model =
|
||||
(await _testCreateModel('static_function_files/hello.dart')).ngDeps;
|
||||
|
||||
var functionReflectable =
|
||||
model.reflectables.firstWhere((i) => i.isFunction, orElse: () => null);
|
||||
|
@ -295,9 +305,7 @@ void allTests() {
|
|||
});
|
||||
|
||||
it('should find direcive aliases patterns.', () async {
|
||||
var ngMeta = new NgMeta.empty();
|
||||
await _testCreateModel('directive_aliases_files/hello.dart',
|
||||
ngMeta: ngMeta);
|
||||
var ngMeta = await _testCreateModel('directive_aliases_files/hello.dart');
|
||||
|
||||
expect(ngMeta.aliases).toContain('alias1');
|
||||
expect(ngMeta.aliases['alias1']).toContain('HelloCmp');
|
||||
|
@ -307,8 +315,7 @@ void allTests() {
|
|||
});
|
||||
|
||||
it('should include hooks for implemented types (single)', () async {
|
||||
var ngMeta = new NgMeta.empty();
|
||||
await _testCreateModel('interfaces_files/soup.dart', ngMeta: ngMeta);
|
||||
var ngMeta = await _testCreateModel('interfaces_files/soup.dart');
|
||||
|
||||
expect(ngMeta.types.isNotEmpty).toBeTrue();
|
||||
expect(ngMeta.types['ChangingSoupComponent']).toBeNotNull();
|
||||
|
@ -318,9 +325,8 @@ void allTests() {
|
|||
});
|
||||
|
||||
it('should include hooks for implemented types (many)', () async {
|
||||
var ngMeta = new NgMeta.empty();
|
||||
await _testCreateModel('multiple_interface_lifecycle_files/soup.dart',
|
||||
ngMeta: ngMeta);
|
||||
var ngMeta = await _testCreateModel(
|
||||
'multiple_interface_lifecycle_files/soup.dart');
|
||||
|
||||
expect(ngMeta.types.isNotEmpty).toBeTrue();
|
||||
expect(ngMeta.types['MultiSoupComponent']).toBeNotNull();
|
||||
|
@ -335,9 +341,9 @@ void allTests() {
|
|||
fakeReader
|
||||
..addAsset(new AssetId('other_package', 'lib/template.html'), '')
|
||||
..addAsset(new AssetId('other_package', 'lib/template.css'), '');
|
||||
var ngMeta = new NgMeta.empty();
|
||||
await _testCreateModel('absolute_url_expression_files/hello.dart',
|
||||
ngMeta: ngMeta, reader: fakeReader);
|
||||
var ngMeta = await _testCreateModel(
|
||||
'absolute_url_expression_files/hello.dart',
|
||||
reader: fakeReader);
|
||||
|
||||
expect(ngMeta.types.isNotEmpty).toBeTrue();
|
||||
expect(ngMeta.types['HelloCmp']).toBeNotNull();
|
||||
|
@ -346,9 +352,7 @@ void allTests() {
|
|||
|
||||
it('should populate all provided values for Components & Directives',
|
||||
() async {
|
||||
var ngMeta = new NgMeta.empty();
|
||||
await _testCreateModel('unusual_component_files/hello.dart',
|
||||
ngMeta: ngMeta);
|
||||
var ngMeta = await _testCreateModel('unusual_component_files/hello.dart');
|
||||
|
||||
expect(ngMeta.types.isNotEmpty).toBeTrue();
|
||||
|
||||
|
@ -382,8 +386,7 @@ void allTests() {
|
|||
});
|
||||
|
||||
it('should include hooks for implemented types (single)', () async {
|
||||
var ngMeta = new NgMeta.empty();
|
||||
await _testCreateModel('interfaces_files/soup.dart', ngMeta: ngMeta);
|
||||
var ngMeta = await _testCreateModel('interfaces_files/soup.dart');
|
||||
|
||||
expect(ngMeta.types.isNotEmpty).toBeTrue();
|
||||
expect(ngMeta.types['ChangingSoupComponent']).toBeNotNull();
|
||||
|
@ -393,9 +396,8 @@ void allTests() {
|
|||
});
|
||||
|
||||
it('should include hooks for implemented types (many)', () async {
|
||||
var ngMeta = new NgMeta.empty();
|
||||
await _testCreateModel('multiple_interface_lifecycle_files/soup.dart',
|
||||
ngMeta: ngMeta);
|
||||
var ngMeta = await _testCreateModel(
|
||||
'multiple_interface_lifecycle_files/soup.dart');
|
||||
|
||||
expect(ngMeta.types.isNotEmpty).toBeTrue();
|
||||
expect(ngMeta.types['MultiSoupComponent']).toBeNotNull();
|
||||
|
@ -410,9 +412,9 @@ void allTests() {
|
|||
fakeReader
|
||||
..addAsset(new AssetId('other_package', 'lib/template.html'), '')
|
||||
..addAsset(new AssetId('other_package', 'lib/template.css'), '');
|
||||
var ngMeta = new NgMeta.empty();
|
||||
await _testCreateModel('absolute_url_expression_files/hello.dart',
|
||||
ngMeta: ngMeta, reader: fakeReader);
|
||||
var ngMeta = await _testCreateModel(
|
||||
'absolute_url_expression_files/hello.dart',
|
||||
reader: fakeReader);
|
||||
|
||||
expect(ngMeta.types.isNotEmpty).toBeTrue();
|
||||
expect(ngMeta.types['HelloCmp']).toBeNotNull();
|
||||
|
@ -423,12 +425,11 @@ void allTests() {
|
|||
});
|
||||
}
|
||||
|
||||
Future<NgDepsModel> _testCreateModel(String inputPath,
|
||||
Future<NgMeta> _testCreateModel(String inputPath,
|
||||
{List<AnnotationDescriptor> customDescriptors: const [],
|
||||
AssetId assetId,
|
||||
AssetReader reader,
|
||||
BuildLogger logger,
|
||||
NgMeta ngMeta}) {
|
||||
BuildLogger logger}) {
|
||||
if (logger == null) logger = new RecordingLogger();
|
||||
return log.setZoned(logger, () async {
|
||||
var inputId = _assetIdForPath(inputPath);
|
||||
|
@ -439,12 +440,9 @@ Future<NgDepsModel> _testCreateModel(String inputPath,
|
|||
reader.addAsset(assetId, await reader.readAsString(inputId));
|
||||
inputId = assetId;
|
||||
}
|
||||
if (ngMeta == null) {
|
||||
ngMeta = new NgMeta.empty();
|
||||
}
|
||||
|
||||
var annotationMatcher = new AnnotationMatcher()..addAll(customDescriptors);
|
||||
return createNgDeps(reader, inputId, annotationMatcher, ngMeta);
|
||||
return createNgDeps(reader, inputId, annotationMatcher);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -114,24 +114,25 @@ void allTests() {
|
|||
new IntegrationTestConfig(
|
||||
'should handle Directive depenedencies declared on a View.',
|
||||
inputs: {
|
||||
'a|web/index.dart': 'directive_dep_files/index.dart',
|
||||
'a|web/foo.dart': 'directive_dep_files/foo.dart',
|
||||
'a|web/bar.dart': 'directive_dep_files/bar.dart'
|
||||
},
|
||||
'a|web/index.dart': 'directive_dep_files/index.dart',
|
||||
'a|web/foo.dart': 'directive_dep_files/foo.dart',
|
||||
'a|web/bar.dart': 'directive_dep_files/bar.dart'
|
||||
},
|
||||
outputs: {
|
||||
'a|web/bar.ng_deps.dart': 'directive_dep_files/expected/bar.ng_deps.dart'
|
||||
}),
|
||||
'a|web/bar.ng_deps.dart': 'directive_dep_files/expected/bar.ng_deps.dart'
|
||||
}),
|
||||
new IntegrationTestConfig(
|
||||
'should handle chained Directive dependencies declared on a View.',
|
||||
inputs: {
|
||||
'a|web/index.dart': 'directive_chain_files/index.dart',
|
||||
'a|web/foo.dart': 'directive_chain_files/foo.dart',
|
||||
'a|web/bar.dart': 'directive_chain_files/bar.dart',
|
||||
'a|web/baz.dart': 'directive_chain_files/baz.dart'
|
||||
},
|
||||
'a|web/index.dart': 'directive_chain_files/index.dart',
|
||||
'a|web/foo.dart': 'directive_chain_files/foo.dart',
|
||||
'a|web/bar.dart': 'directive_chain_files/bar.dart',
|
||||
'a|web/baz.dart': 'directive_chain_files/baz.dart'
|
||||
},
|
||||
outputs: {
|
||||
'a|web/bar.ng_deps.dart': 'directive_chain_files/expected/bar.ng_deps.dart'
|
||||
})
|
||||
'a|web/bar.ng_deps.dart':
|
||||
'directive_chain_files/expected/bar.ng_deps.dart'
|
||||
})
|
||||
];
|
||||
|
||||
var cache = {};
|
||||
|
@ -160,8 +161,9 @@ void allTests() {
|
|||
],
|
||||
config.assetPathToInputPath,
|
||||
config.assetPathToExpectedOutputPath,
|
||||
[],
|
||||
StringFormatter.noNewlinesOrSurroundingWhitespace);
|
||||
[]);
|
||||
//,
|
||||
// StringFormatter.noNewlinesOrSurroundingWhitespace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'bar.template.dart' as _templates;
|
|||
import 'bar.dart';
|
||||
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
|
||||
import 'package:angular2/src/core/metadata.dart';
|
||||
import 'package:angular2/src/core/metadata.ng_deps.dart' as i0;
|
||||
import 'baz.dart';
|
||||
import 'baz.ng_deps.dart' as i1;
|
||||
export 'bar.dart';
|
||||
|
@ -21,5 +22,6 @@ void initReflector() {
|
|||
const View(directives: [Foo], template: 'foo'),
|
||||
_templates.HostMyComponentTemplate
|
||||
], const [], () => new MyComponent()));
|
||||
i0.initReflector();
|
||||
i1.initReflector();
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'bar.template.dart' as _templates;
|
|||
import 'bar.dart';
|
||||
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
|
||||
import 'package:angular2/src/core/metadata.dart';
|
||||
import 'package:angular2/src/core/metadata.ng_deps.dart' as i0;
|
||||
import 'foo.dart' as prefix;
|
||||
import 'foo.ng_deps.dart' as i1;
|
||||
export 'bar.dart';
|
||||
|
@ -21,5 +22,6 @@ void initReflector() {
|
|||
const View(directives: [prefix.Foo], template: 'foo'),
|
||||
_templates.HostMyComponentTemplate
|
||||
], const [], () => new MyComponent()));
|
||||
i0.initReflector();
|
||||
i1.initReflector();
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'bar.template.dart' as _templates;
|
|||
import 'bar.dart';
|
||||
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
|
||||
import 'package:angular2/src/core/metadata.dart';
|
||||
import 'package:angular2/src/core/metadata.ng_deps.dart' as i0;
|
||||
export 'bar.dart';
|
||||
|
||||
var _visited = false;
|
||||
|
@ -23,4 +24,5 @@ void initReflector() {
|
|||
], const [], () => new MyComponent()))
|
||||
..registerGetters(
|
||||
{'eventName1': (o) => o.eventName1, 'eventName2': (o) => o.eventName2});
|
||||
i0.initReflector();
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'bar.template.dart' as _templates;
|
|||
import 'bar.dart';
|
||||
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
|
||||
import 'package:angular2/src/core/metadata.dart';
|
||||
import 'package:angular2/src/core/metadata.ng_deps.dart' as i0;
|
||||
import 'foo.dart';
|
||||
export 'bar.dart';
|
||||
|
||||
|
@ -22,4 +23,5 @@ void initReflector() {
|
|||
], const [
|
||||
const [MyContext]
|
||||
], (MyContext c) => new MyComponent(c)));
|
||||
i0.initReflector();
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'bar.template.dart' as _templates;
|
|||
import 'bar.dart';
|
||||
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
|
||||
import 'package:angular2/src/core/metadata.dart';
|
||||
import 'package:angular2/src/core/metadata.ng_deps.dart' as i0;
|
||||
export 'bar.dart';
|
||||
|
||||
var _visited = false;
|
||||
|
@ -19,4 +20,5 @@ void initReflector() {
|
|||
const View(template: ''),
|
||||
_templates.HostMyComponentTemplate
|
||||
], const [], () => new MyComponent()));
|
||||
i0.initReflector();
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'index.dart';
|
|||
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
|
||||
import 'package:angular2/bootstrap_static.dart';
|
||||
import 'index.ng_deps.dart' as ngStaticInit;
|
||||
import 'index.ng_deps.dart' as i1;
|
||||
import 'package:angular2/src/core/reflection/reflection.dart';
|
||||
import 'bar.dart';
|
||||
import 'bar.ng_deps.dart' as i3;
|
||||
|
@ -13,5 +14,6 @@ var _visited = false;
|
|||
void initReflector() {
|
||||
if (_visited) return;
|
||||
_visited = true;
|
||||
i1.initReflector();
|
||||
i3.initReflector();
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'bar.template.dart' as _templates;
|
|||
import 'bar.dart';
|
||||
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
|
||||
import 'package:angular2/src/core/metadata.dart';
|
||||
import 'package:angular2/src/core/metadata.ng_deps.dart' as i0;
|
||||
export 'bar.dart';
|
||||
|
||||
var _visited = false;
|
||||
|
@ -19,4 +20,5 @@ void initReflector() {
|
|||
const View(template: ''),
|
||||
_templates.HostMyComponentTemplate
|
||||
], const [], () => new MyComponent()));
|
||||
i0.initReflector();
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'bar.template.dart' as _templates;
|
|||
import 'bar.dart';
|
||||
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
|
||||
import 'package:angular2/src/core/metadata.dart';
|
||||
import 'package:angular2/src/core/metadata.ng_deps.dart' as i0;
|
||||
export 'bar.dart';
|
||||
|
||||
var _visited = false;
|
||||
|
@ -19,4 +20,5 @@ void initReflector() {
|
|||
const View(template: 'Salad: {{myNum}} is awesome'),
|
||||
_templates.HostMyComponentTemplate
|
||||
], const [], () => new MyComponent()));
|
||||
i0.initReflector();
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'bar.template.dart' as _templates;
|
|||
import 'bar.dart';
|
||||
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
|
||||
import 'package:angular2/src/core/metadata.dart';
|
||||
import 'package:angular2/src/core/metadata.ng_deps.dart' as i0;
|
||||
import 'foo.dart' as prefix;
|
||||
export 'bar.dart';
|
||||
|
||||
|
@ -27,4 +28,5 @@ void initReflector() {
|
|||
],
|
||||
(prefix.MyContext c, String inValue) =>
|
||||
new MyComponent(c, inValue)));
|
||||
i0.initReflector();
|
||||
}
|
||||
|
|
|
@ -6,9 +6,9 @@ import 'package:unittest/vm_config.dart';
|
|||
|
||||
import 'common/async_string_writer_tests.dart' as asyncStringWriter;
|
||||
import 'common/ng_meta_test.dart' as ngMetaTest;
|
||||
import 'common/url_resolver_tests.dart' as urlResolver;
|
||||
import 'bind_generator/all_tests.dart' as bindGenerator;
|
||||
import 'deferred_rewriter/all_tests.dart' as deferredRewriter;
|
||||
import 'directive_linker/all_tests.dart' as directiveLinker;
|
||||
import 'directive_metadata_linker/all_tests.dart' as directiveMeta;
|
||||
import 'directive_processor/all_tests.dart' as directiveProcessor;
|
||||
import 'inliner_for_test/all_tests.dart' as inliner;
|
||||
|
@ -22,7 +22,6 @@ main() {
|
|||
describe('AsyncStringWriter', asyncStringWriter.allTests);
|
||||
describe('NgMeta', ngMetaTest.allTests);
|
||||
describe('Bind Generator', bindGenerator.allTests);
|
||||
describe('Directive Linker', directiveLinker.allTests);
|
||||
describe('Directive Metadata Linker', directiveMeta.allTests);
|
||||
describe('Directive Processor', directiveProcessor.allTests);
|
||||
describe('Inliner For Test', inliner.allTests);
|
||||
|
@ -30,6 +29,7 @@ main() {
|
|||
describe('Template Compiler', templateCompiler.allTests);
|
||||
describe('Deferred Rewriter', deferredRewriter.allTests);
|
||||
describe('Stylesheet Compiler', stylesheetCompiler.allTests);
|
||||
describe('Url Resolver', urlResolver.allTests);
|
||||
// NOTE(kegluneq): These use `code_transformers#testPhases`, which is not
|
||||
// designed to work with `guinness`.
|
||||
group('Transformer Pipeline', integration.allTests);
|
||||
|
|
Loading…
Reference in New Issue