refactor(dart/transform): Use protos to run & test directive_linker

Use the proto ng_deps model objects internally and while testing the directive
linker rather than reading & comparing Dart code.
This commit is contained in:
Tim Blasi 2015-09-15 16:54:26 -07:00
parent cc0c30484f
commit 820b30c181
23 changed files with 234 additions and 332 deletions

View File

@ -140,7 +140,7 @@ abstract class NgDepsWriterMixin
} }
// We do not support `partUris`, so skip outputting them. // We do not support `partUris`, so skip outputting them.
model.imports.forEach((importModel) { for (var importModel in model.imports) {
// Ignore deferred imports here so as to not load the deferred libraries // Ignore deferred imports here so as to not load the deferred libraries
// code in the current library causing much of the code to not be // code in the current library causing much of the code to not be
// deferred. Instead `DeferredRewriter` will rewrite the code as to load // deferred. Instead `DeferredRewriter` will rewrite the code as to load
@ -148,7 +148,7 @@ abstract class NgDepsWriterMixin
if (importModel.isDeferred) return; if (importModel.isDeferred) return;
writeImportModel(importModel); writeImportModel(importModel);
}); }
model.exports.forEach(writeExportModel); model.exports.forEach(writeExportModel);
buffer buffer
@ -162,6 +162,13 @@ abstract class NgDepsWriterMixin
buffer.writeln(';'); buffer.writeln(';');
} }
// Call the setup method for our imports that are `.ng_deps` imports.
for (var importModel in model.imports) {
if (importModel.isNgDeps) {
buffer.writeln('${importModel.prefix}.${SETUP_METHOD_NAME}();');
}
}
buffer.writeln('}'); buffer.writeln('}');
} }
} }

View File

@ -12,6 +12,7 @@ class ImportModel extends GeneratedMessage {
..p(3, 'hideCombinators', PbFieldType.PS) ..p(3, 'hideCombinators', PbFieldType.PS)
..a(4, 'prefix', PbFieldType.OS) ..a(4, 'prefix', PbFieldType.OS)
..a(5, 'isDeferred', PbFieldType.OB) ..a(5, 'isDeferred', PbFieldType.OB)
..a(6, 'isNgDeps', PbFieldType.OB)
; ;
ImportModel() : super(); ImportModel() : super();
@ -48,6 +49,11 @@ class ImportModel extends GeneratedMessage {
void set isDeferred(bool v) { setField(5, v); } void set isDeferred(bool v) { setField(5, v); }
bool hasIsDeferred() => hasField(5); bool hasIsDeferred() => hasField(5);
void clearIsDeferred() => clearField(5); void clearIsDeferred() => clearField(5);
bool get isNgDeps => getField(6);
void set isNgDeps(bool v) { setField(6, v); }
bool hasIsNgDeps() => hasField(6);
void clearIsNgDeps() => clearField(6);
} }
class _ReadonlyImportModel extends ImportModel with ReadonlyMessageMixin {} class _ReadonlyImportModel extends ImportModel with ReadonlyMessageMixin {}
@ -95,6 +101,7 @@ const ImportModel$json = const {
const {'1': 'hide_combinators', '3': 3, '4': 3, '5': 9}, const {'1': 'hide_combinators', '3': 3, '4': 3, '5': 9},
const {'1': 'prefix', '3': 4, '4': 1, '5': 9}, const {'1': 'prefix', '3': 4, '4': 1, '5': 9},
const {'1': 'is_deferred', '3': 5, '4': 1, '5': 8}, const {'1': 'is_deferred', '3': 5, '4': 1, '5': 8},
const {'1': 'is_ng_deps', '3': 6, '4': 1, '5': 8},
], ],
}; };
@ -109,7 +116,7 @@ const ExportModel$json = const {
/** /**
* Generated with: * Generated with:
* import_export_model.proto (36a3a72d0884b84b451b7188ffa1fc93b44e7b62) * import_export_model.proto (c018d2ad92db2d341631d813ace70925d1ccbb9b)
* libprotoc 2.5.0 * libprotoc 2.5.0
* dart-protoc-plugin (cc35f743de982a4916588b9c505dd21c7fe87d17) * dart-protoc-plugin (cc35f743de982a4916588b9c505dd21c7fe87d17)
*/ */

View File

@ -16,6 +16,8 @@ message ImportModel {
optional string prefix = 4; optional string prefix = 4;
optional bool is_deferred = 5; optional bool is_deferred = 5;
optional bool is_ng_deps = 6;
} }
// See message above about wire-compatiblity with `ImportModel`. // See message above about wire-compatiblity with `ImportModel`.

View File

@ -5,6 +5,7 @@ const SETUP_METHOD_NAME = 'initReflector';
const REFLECTOR_VAR_NAME = 'reflector'; const REFLECTOR_VAR_NAME = 'reflector';
const TRANSFORM_DYNAMIC_MODE = 'transform_dynamic'; const TRANSFORM_DYNAMIC_MODE = 'transform_dynamic';
const DEPS_EXTENSION = '.ng_deps.dart'; const DEPS_EXTENSION = '.ng_deps.dart';
const DEPS_JSON_EXTENSION = '.ng_deps.json';
const META_EXTENSION = '.ng_meta.json'; const META_EXTENSION = '.ng_meta.json';
// TODO(sigmund): consider merging into .ng_meta by generating local metadata // TODO(sigmund): consider merging into .ng_meta by generating local metadata
// upfront (rather than extracting it from ng_deps). // upfront (rather than extracting it from ng_deps).
@ -19,16 +20,19 @@ const REGISTER_METHODS_METHOD_NAME = 'registerMethods';
/// Returns `uri` with its extension updated to [META_EXTENSION]. /// Returns `uri` with its extension updated to [META_EXTENSION].
String toMetaExtension(String uri) => String toMetaExtension(String uri) =>
_toExtension(uri, const [DEPS_EXTENSION, '.dart'], META_EXTENSION); _toExtension(uri, const [DEPS_EXTENSION, DEPS_JSON_EXTENSION, '.dart'], META_EXTENSION);
/// Returns `uri` with its extension updated to [DEPS_EXTENSION]. /// Returns `uri` with its extension updated to [DEPS_EXTENSION].
String toDepsExtension(String uri) => String toDepsExtension(String uri) =>
_toExtension(uri, const [META_EXTENSION, '.dart'], DEPS_EXTENSION); _toExtension(uri, const [META_EXTENSION, DEPS_JSON_EXTENSION, '.dart'], DEPS_EXTENSION);
/// Returns `uri` with its extension updated to [ALIAS_EXTENSION]. /// Returns `uri` with its extension updated to [ALIAS_EXTENSION].
String toAliasExtension(String uri) => String toAliasExtension(String uri) =>
_toExtension(uri, const [DEPS_EXTENSION, '.dart'], ALIAS_EXTENSION); _toExtension(uri, const [DEPS_EXTENSION, '.dart'], ALIAS_EXTENSION);
String toJsonExtension(String uri) =>
_toExtension(uri, const [DEPS_EXTENSION, '.dart'], DEPS_JSON_EXTENSION);
/// Returns `uri` with its extension updated to `toExtension` if its /// Returns `uri` with its extension updated to `toExtension` if its
/// extension is currently in `fromExtension`. /// extension is currently in `fromExtension`.
String _toExtension( String _toExtension(

View File

@ -2,118 +2,115 @@ library angular2.transform.directive_linker.linker;
import 'dart:async'; import 'dart:async';
import 'package:analyzer/analyzer.dart';
import 'package:angular2/src/transform/common/asset_reader.dart'; import 'package:angular2/src/transform/common/asset_reader.dart';
import 'package:angular2/src/transform/common/logging.dart'; import 'package:angular2/src/transform/common/logging.dart';
import 'package:angular2/src/transform/common/names.dart'; import 'package:angular2/src/transform/common/names.dart';
import 'package:angular2/src/transform/common/ng_deps.dart'; import 'package:angular2/src/transform/common/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:barback/barback.dart';
import 'package:code_transformers/assets.dart'; import 'package:code_transformers/assets.dart';
/// Checks the `.ng_deps.dart` file represented by `entryPoint` and /// Checks the `.ng_deps.json` file represented by `entryPoint` and
/// determines whether it is necessary to the functioning of the Angular 2 /// determines whether it is necessary to the functioning of the Angular 2
/// Dart app. /// Dart app.
/// ///
/// An `.ng_deps.dart` file is not necessary if: /// An `.ng_deps.json` file is not necessary if:
/// 1. It does not register any `@Injectable` types with the system. /// 1. It does not register any `@Injectable` types with the system.
/// 2. It does not import any libraries whose `.ng_deps.dart` files register /// 2. It does not import any libraries whose `.ng_deps.json` files register
/// any `@Injectable` types with the system. /// any `@Injectable` types with the system.
/// ///
/// Since `@Directive` and `@Component` inherit from `@Injectable`, we know /// Since `@Directive` and `@Component` inherit from `@Injectable`, we know
/// we will not miss processing any classes annotated with those tags. /// we will not miss processing any classes annotated with those tags.
Future<bool> isNecessary(AssetReader reader, AssetId entryPoint) async { Future<bool> isNecessary(AssetReader reader, AssetId entryPoint) async {
NgDeps ngDeps = await NgDeps.parse(reader, entryPoint); 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 (ngDeps.registeredTypes.isNotEmpty) return true; if (ngDepsModel.reflectables != null &&
ngDepsModel.reflectables.isNotEmpty) return true;
// We do not register any @Injectables, do we call any dependencies? // We do not register any @Injectables, do we call any dependencies?
var linkedDepsMap = var linkedDepsMap = await _processNgImports(reader, entryPoint, ngDepsModel);
await _processNgImports(reader, entryPoint, _getSortedDeps(ngDeps));
return linkedDepsMap.isNotEmpty; return linkedDepsMap.isNotEmpty;
} }
/// Modifies the `.ng_deps.dart` file represented by `entryPoint` to call its /// Modifies the [NgDepsModel] represented by `entryPoint` to import its
/// dependencies associated `initReflector` methods. /// dependencies' associated `.ng_deps.dart` files.
/// ///
/// For example, if entry_point.ng_deps.dart imports dependency.dart, this /// For example, if entry_point.ng_deps.dart imports dependency.dart, this
/// will check if dependency.ng_deps.dart exists. If it does, we add: /// 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
/// import 'dependency.ng_deps.dart' as i0; /// call `initReflector`.
/// ... Future<NgDepsModel> linkNgDeps(AssetReader reader, AssetId entryPoint) async {
/// void setupReflection(reflector) { if (!(await reader.hasInput(entryPoint))) return null;
/// ... var jsonString = await reader.readAsString(entryPoint);
/// i0.initReflector(reflector); if (jsonString.isEmpty) return null;
/// } var ngDepsModel = new NgDepsModel.fromJson(jsonString);
/// ```
Future<String> linkNgDeps(AssetReader reader, AssetId entryPoint) async {
NgDeps ngDeps = await NgDeps.parse(reader, entryPoint);
if (ngDeps == null) return null; var linkedDepsMap = await _processNgImports(reader, entryPoint, ngDepsModel);
var allDeps = _getSortedDeps(ngDeps);
var linkedDepsMap = await _processNgImports(reader, entryPoint, allDeps);
if (linkedDepsMap.isEmpty) { if (linkedDepsMap.isEmpty) {
// We are not calling `initReflector` on any other libraries. // We are not calling `initReflector` on any other libraries, but we still
return ngDeps.code; // return the model to ensure it is written to code.
// TODO(kegluneq): Continue using the protobuf format after this phase.
return ngDepsModel;
} }
var importBuf = new StringBuffer(); for (var i = ngDepsModel.imports.length - 1; i >= 0; --i) {
var declarationBuf = new StringBuffer(); var import = ngDepsModel.imports[i];
var code = ngDeps.code; if (linkedDepsMap.containsKey(import.uri)) {
var codeIdx = 0; var linkedModel = new ImportModel()
// Generate import statements for linked deps where necessary. ..isNgDeps = true
for (var i = 0, it = allDeps.iterator; it.moveNext();) { ..uri = toDepsExtension(linkedDepsMap[import.uri])
if (linkedDepsMap.containsKey(it.current)) { ..prefix = 'i$i';
importBuf.write(code.substring(codeIdx, it.current.end)); // TODO(kegluneq): Preserve combinators?
codeIdx = it.current.end; ngDepsModel.imports.insert(i + 1, linkedModel);
importBuf.write(''' }
import '${linkedDepsMap[it.current]}' as i${i}; }
'''); for (var i = 0, iLen = ngDepsModel.exports.length; i < iLen; ++i) {
declarationBuf.write('i${i}.${SETUP_METHOD_NAME}();'); var export = ngDepsModel.exports[i];
++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);
} }
} }
var declarationSeamIdx = ngDeps.setupMethod.end - 1; return ngDepsModel;
return '$importBuf'
'${code.substring(codeIdx, declarationSeamIdx)}'
'$declarationBuf'
'${code.substring(declarationSeamIdx)}';
} }
/// All `import`s and `export`s in `ngDeps` sorted by order of appearance in bool _isNotDartDirective(dynamic model) {
/// the file. return !model.uri.startsWith('dart:');
List<UriBasedDirective> _getSortedDeps(NgDeps ngDeps) {
return <UriBasedDirective>[]
..addAll(ngDeps.imports)
..addAll(ngDeps.exports)
..sort((a, b) => a.end.compareTo(b.end));
} }
bool _isNotDartDirective(UriBasedDirective directive) { /// Maps the `uri` of each input [ImportModel] or [ExportModel] to its
return !stringLiteralToString(directive.uri).startsWith('dart:'); /// associated `.ng_deps.json` file, if one exists.
} Future<Map<String, String>> _processNgImports(
AssetReader reader, AssetId ngJsonAsset, NgDepsModel model) {
/// Maps each input {@link UriBasedDirective} to its associated `.ng_deps.dart`
/// file, if it exists.
Future<Map<UriBasedDirective, String>> _processNgImports(AssetReader reader,
AssetId entryPoint, Iterable<UriBasedDirective> directives) {
final nullFuture = new Future.value(null); final nullFuture = new Future.value(null);
final retVal = <UriBasedDirective, String>{}; 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 return Future
.wait(directives .wait(
.where(_isNotDartDirective) importsAndExports.where(_isNotDartDirective).map((dynamic directive) {
.map((UriBasedDirective directive) { // The uri of the import or export with .dart replaced with .ng_deps.json.
var ngDepsUri = toDepsExtension(stringLiteralToString(directive.uri)); // This is the json file containing Angular 2 codegen info, if one exists.
var linkedJsonUri = toJsonExtension(directive.uri);
var spanArg = null; var spanArg = null;
var ngDepsAsset = uriToAssetId(entryPoint, ngDepsUri, logger, spanArg, var linkedNgJsonAsset = uriToAssetId(
entryPoint, linkedJsonUri, logger, spanArg,
errorOnAbsolute: false); errorOnAbsolute: false);
if (ngDepsAsset == entryPoint) return nullFuture; if (linkedNgJsonAsset == ngJsonAsset) return nullFuture;
return reader.hasInput(ngDepsAsset).then((hasInput) { return reader.hasInput(linkedNgJsonAsset).then((hasInput) {
if (hasInput) { if (hasInput) {
retVal[directive] = ngDepsUri; retVal[directive.uri] = linkedJsonUri;
} }
}, onError: (_) => null); }, onError: (_) => null);
})) }))

View File

@ -3,6 +3,7 @@ library angular2.transform.directive_linker.transformer;
import 'dart:async'; import 'dart:async';
import 'package:angular2/src/transform/common/asset_reader.dart'; import 'package:angular2/src/transform/common/asset_reader.dart';
import 'package:angular2/src/transform/common/code/ng_deps_code.dart';
import 'package:angular2/src/transform/common/formatter.dart'; import 'package:angular2/src/transform/common/formatter.dart';
import 'package:angular2/src/transform/common/logging.dart' as log; import 'package:angular2/src/transform/common/logging.dart' as log;
import 'package:angular2/src/transform/common/names.dart'; import 'package:angular2/src/transform/common/names.dart';
@ -10,15 +11,14 @@ import 'package:barback/barback.dart';
import 'linker.dart'; import 'linker.dart';
/// Transformer responsible for processing .ng_deps.dart files created by /// Transformer responsible for processing `.ng_deps.json` files created by
/// {@link DirectiveProcessor} and ensuring that the generated calls to /// {@link DirectiveProcessor} and ensuring that each imports its dependencies'
/// `setupReflection` call the necessary `setupReflection` method in all /// .ng_deps.dart files.
/// dependencies.
class DirectiveLinker extends Transformer implements DeclaringTransformer { class DirectiveLinker extends Transformer implements DeclaringTransformer {
DirectiveLinker(); DirectiveLinker();
@override @override
bool isPrimary(AssetId id) => id.path.endsWith(DEPS_EXTENSION); bool isPrimary(AssetId id) => id.path.endsWith(DEPS_JSON_EXTENSION);
@override @override
declareOutputs(DeclaringTransform transform) { declareOutputs(DeclaringTransform transform) {
@ -31,22 +31,27 @@ class DirectiveLinker extends Transformer implements DeclaringTransformer {
var reader = new AssetReader.fromTransform(transform); var reader = new AssetReader.fromTransform(transform);
var assetId = transform.primaryInput.id; var assetId = transform.primaryInput.id;
var assetPath = assetId.path; var assetPath = assetId.path;
var transformedCode = await linkNgDeps(reader, assetId); var ngDepsModel = await linkNgDeps(reader, assetId);
if (transformedCode != null) { if (ngDepsModel != null) {
var formattedCode = formatter.format(transformedCode, uri: assetPath); var buf = new StringBuffer();
transform.addOutput(new Asset.fromString(assetId, formattedCode)); var writer = new NgDepsWriter(buf);
writer.writeNgDepsModel(ngDepsModel);
var formattedCode = formatter.format('$buf', uri: assetPath);
var ngDepsAssetId =
new AssetId(assetId.package, toDepsExtension(assetPath));
transform.addOutput(new Asset.fromString(ngDepsAssetId, formattedCode));
} }
}); });
} }
} }
/// Transformer responsible for removing unnecessary `.ng_deps.dart` files /// Transformer responsible for removing unnecessary `.ng_deps.json` files
/// created by {@link DirectiveProcessor}. /// created by {@link DirectiveProcessor}.
class EmptyNgDepsRemover extends Transformer implements DeclaringTransformer { class EmptyNgDepsRemover extends Transformer implements DeclaringTransformer {
EmptyNgDepsRemover(); EmptyNgDepsRemover();
@override @override
bool isPrimary(AssetId id) => id.path.endsWith(DEPS_EXTENSION); bool isPrimary(AssetId id) => id.path.endsWith(DEPS_JSON_EXTENSION);
/// We occasionally consume the primary input, but that depends on the /// We occasionally consume the primary input, but that depends on the
/// contents of the file, so we conservatively do not declare any outputs nor /// contents of the file, so we conservatively do not declare any outputs nor

View File

@ -51,6 +51,9 @@ Future<NgDepsModel> createNgDeps(AssetReader reader, AssetId assetId,
parsedCode.accept(ngDepsVisitor); parsedCode.accept(ngDepsVisitor);
var ngDepsModel = ngDepsVisitor.model; var ngDepsModel = ngDepsVisitor.model;
var ngMetaVisitor = new _NgMetaVisitor(ngMeta);
parsedCode.accept(ngMetaVisitor);
// If this file imports only dart: libraries and does not define any // 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. // reflectables of its own, it doesn't need a .ng_deps.dart file.
if (!directivesVisitor.usesNonLangLibs && if (!directivesVisitor.usesNonLangLibs &&
@ -58,9 +61,6 @@ Future<NgDepsModel> createNgDeps(AssetReader reader, AssetId assetId,
return null; return null;
} }
var ngMetaVisitor = new _NgMetaVisitor(ngMeta);
parsedCode.accept(ngMetaVisitor);
if (inlineViews) { if (inlineViews) {
await inlineViewProps(new XhrImpl(reader, assetId), ngDepsModel); await inlineViewProps(new XhrImpl(reader, assetId), ngDepsModel);
} }

View File

@ -4,7 +4,6 @@ import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'package:angular2/src/transform/common/asset_reader.dart'; import 'package:angular2/src/transform/common/asset_reader.dart';
import 'package:angular2/src/transform/common/code/ng_deps_code.dart';
import 'package:angular2/src/transform/common/logging.dart' as log; import 'package:angular2/src/transform/common/logging.dart' as log;
import 'package:angular2/src/transform/common/names.dart'; import 'package:angular2/src/transform/common/names.dart';
import 'package:angular2/src/transform/common/options.dart'; import 'package:angular2/src/transform/common/options.dart';
@ -52,15 +51,12 @@ class DirectiveProcessor extends Transformer implements DeclaringTransformer {
inlineViews: options.inlineViews); inlineViews: options.inlineViews);
if (ngDepsModel != null) { if (ngDepsModel != null) {
var ngDepsAssetId = var ngDepsAssetId =
transform.primaryInput.id.changeExtension(DEPS_EXTENSION); transform.primaryInput.id.changeExtension(DEPS_JSON_EXTENSION);
if (await transform.hasInput(ngDepsAssetId)) { if (await transform.hasInput(ngDepsAssetId)) {
log.logger.error('Clobbering ${ngDepsAssetId}. ' log.logger.error('Clobbering ${ngDepsAssetId}. '
'This probably will not end well'); 'This probably will not end well');
} }
var buf = new StringBuffer(); transform.addOutput(new Asset.fromString(ngDepsAssetId, ngDepsModel.writeToJson()));
var writer = new NgDepsWriter(buf);
writer.writeNgDepsModel(ngDepsModel);
transform.addOutput(new Asset.fromString(ngDepsAssetId, '$buf'));
} }
if (!ngMeta.isEmpty) { if (!ngMeta.isEmpty) {
var ngAliasesId = var ngAliasesId =

View File

@ -32,11 +32,8 @@ class AngularTransformerGroup extends TransformerGroup {
phases.addAll(new List.generate( phases.addAll(new List.generate(
options.optimizationPhases, (_) => [new EmptyNgDepsRemover()])); options.optimizationPhases, (_) => [new EmptyNgDepsRemover()]));
phases.addAll([ phases.addAll([
[ [new DirectiveLinker(), new DeferredRewriter(options)],
new DirectiveLinker(), [new DirectiveMetadataExtractor()],
new DirectiveMetadataExtractor(),
new DeferredRewriter(options)
],
[new BindGenerator(options)], [new BindGenerator(options)],
[new TemplateCompiler(options)] [new TemplateCompiler(options)]
]); ]);

View File

@ -4,6 +4,7 @@ import 'package:barback/barback.dart';
import 'package:angular2/src/transform/common/model/annotation_model.pb.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/import_export_model.pb.dart';
import 'package:angular2/src/transform/common/model/ng_deps_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:angular2/src/transform/directive_linker/linker.dart';
import 'package:dart_style/dart_style.dart'; import 'package:dart_style/dart_style.dart';
import 'package:guinness/guinness.dart'; import 'package:guinness/guinness.dart';
@ -15,36 +16,122 @@ var formatter = new DartFormatter();
main() => allTests(); main() => allTests();
void allTests() { void allTests() {
var reader = new TestAssetReader(); var reader;
it('should ensure that dependencies are property chained.', () async { beforeEach(() {
for (var inputPath in [ reader = new TestAssetReader();
'bar.ng_deps.dart',
'foo.ng_deps.dart',
'index.ng_deps.dart'
]) {
var expected =
readFile('directive_linker/simple_files/expected/$inputPath');
inputPath = 'directive_linker/simple_files/$inputPath';
var actual = formatter
.format(await linkNgDeps(reader, new AssetId('a', inputPath)));
expect(actual).toEqual(formatter.format(expected));
}
}); });
it('should ensure that exported dependencies are property chained.', it('should chain imported dependencies.', () async {
() async { var fooModel = new NgDepsModel()
for (var inputPath in [ ..libraryUri = 'test.foo'
'bar.ng_deps.dart', ..imports.add(new ImportModel()
'foo.ng_deps.dart', ..uri = 'bar.dart'
'index.ng_deps.dart' ..prefix = 'dep');
]) { var barModel = new NgDepsModel()..libraryUri = 'test.bar';
var expected =
readFile('directive_linker/simple_export_files/expected/$inputPath'); var fooAssetId = new AssetId('a', 'lib/foo.ng_deps.json');
inputPath = 'directive_linker/simple_export_files/$inputPath'; reader
var actual = formatter ..addAsset(fooAssetId, fooModel.writeToJson())
.format(await linkNgDeps(reader, new AssetId('a', inputPath))); ..addAsset(
expect(actual).toEqual(formatter.format(expected)); 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();
});
}); });
} }

View File

@ -1,18 +0,0 @@
library bar.ng_deps.dart;
import 'bar.dart';
export 'bar.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/src/core/metadata.dart';
export 'foo.dart';
var _visited = false;
void initReflector() {
if (_visited) return;
_visited = true;
_ngRef.reflector
..registerType(
MyComponent,
new _ngRef.ReflectionInfo(const [const Component(selector: '[soup]')],
const [], () => new MyComponent()));
}

View File

@ -1,20 +0,0 @@
library bar.ng_deps.dart;
import 'bar.dart';
export 'bar.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/src/core/metadata.dart';
export 'foo.dart';
import 'foo.ng_deps.dart' as i0;
var _visited = false;
void initReflector() {
if (_visited) return;
_visited = true;
_ngRef.reflector
..registerType(
MyComponent,
new _ngRef.ReflectionInfo(const [const Component(selector: '[soup]')],
const [], () => new MyComponent()));
i0.initReflector();
}

View File

@ -1,17 +0,0 @@
library foo.ng_deps.dart;
import 'foo.dart';
export 'foo.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/src/core/metadata.dart';
var _visited = false;
void initReflector() {
if (_visited) return;
_visited = true;
_ngRef.reflector
..registerType(
DependencyComponent,
new _ngRef.ReflectionInfo(const [const Component(selector: '[salad]')],
const [], () => new DependencyComponent()));
}

View File

@ -1,14 +0,0 @@
library web_foo.ng_deps.dart;
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/bootstrap_static.dart';
import 'package:angular2/src/core/reflection/reflection_capabilities.dart';
import 'bar.dart';
import 'bar.ng_deps.dart' as i0;
var _visited = false;
void initReflector() {
if (_visited) return;
_visited = true;
i0.initReflector();
}

View File

@ -1,17 +0,0 @@
library foo.ng_deps.dart;
import 'foo.dart';
export 'foo.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/src/core/metadata.dart';
var _visited = false;
void initReflector() {
if (_visited) return;
_visited = true;
_ngRef.reflector
..registerType(
DependencyComponent,
new _ngRef.ReflectionInfo(const [const Component(selector: '[salad]')],
const [], () => new DependencyComponent()));
}

View File

@ -1,12 +0,0 @@
library web_foo.ng_deps.dart;
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/bootstrap_static.dart';
import 'package:angular2/src/core/reflection/reflection_capabilities.dart';
import 'bar.dart';
var _visited = false;
void initReflector() {
if (_visited) return;
_visited = true;
}

View File

@ -1,20 +0,0 @@
library bar.ng_deps.dart;
import 'bar.dart';
export 'bar.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/src/core/metadata.dart';
import 'foo.dart' as dep;
var _visited = false;
void initReflector() {
if (_visited) return;
_visited = true;
_ngRef.reflector
..registerType(
MyComponent,
new ReflectionInfo(const [
const Component(
selector: '[soup]', viewBindings: const [dep.DependencyComponent])
], const [], () => new MyComponent()));
}

View File

@ -1,22 +0,0 @@
library bar.ng_deps.dart;
import 'bar.dart';
export 'bar.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/src/core/metadata.dart';
import 'foo.dart' as dep;
import 'foo.ng_deps.dart' as i0;
var _visited = false;
void initReflector() {
if (_visited) return;
_visited = true;
_ngRef.reflector
..registerType(
MyComponent,
new ReflectionInfo(const [
const Component(
selector: '[soup]', viewBindings: const [dep.DependencyComponent])
], const [], () => new MyComponent()));
i0.initReflector();
}

View File

@ -1,17 +0,0 @@
library foo.ng_deps.dart;
import 'foo.dart';
export 'foo.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/src/core/metadata.dart';
var _visited = false;
void initReflector() {
if (_visited) return;
_visited = true;
_ngRef.reflector
..registerType(
DependencyComponent,
new _ngRef.ReflectionInfo(const [const Component(selector: '[salad]')],
const [], () => new DependencyComponent()));
}

View File

@ -1,14 +0,0 @@
library web_foo.ng_deps.dart;
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/bootstrap_static.dart';
import 'package:angular2/src/core/reflection/reflection_capabilities.dart';
import 'bar.dart';
import 'bar.ng_deps.dart' as i0;
var _visited = false;
void initReflector() {
if (_visited) return;
_visited = true;
i0.initReflector();
}

View File

@ -1,17 +0,0 @@
library foo.ng_deps.dart;
import 'foo.dart';
export 'foo.dart';
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/src/core/metadata.dart';
var _visited = false;
void initReflector() {
if (_visited) return;
_visited = true;
_ngRef.reflector
..registerType(
DependencyComponent,
new _ngRef.ReflectionInfo(const [const Component(selector: '[salad]')],
const [], () => new DependencyComponent()));
}

View File

@ -1,12 +0,0 @@
library web_foo.ng_deps.dart;
import 'package:angular2/src/core/reflection/reflection.dart' as _ngRef;
import 'package:angular2/bootstrap_static.dart';
import 'package:angular2/src/core/reflection/reflection_capabilities.dart';
import 'bar.dart';
var _visited = false;
void initReflector() {
if (_visited) return;
_visited = true;
}

View File

@ -6,12 +6,12 @@ import 'package:angular2/bootstrap_static.dart';
import 'index.ng_deps.dart' as ngStaticInit; import 'index.ng_deps.dart' as ngStaticInit;
import 'package:angular2/src/core/reflection/reflection.dart'; import 'package:angular2/src/core/reflection/reflection.dart';
import 'bar.dart'; import 'bar.dart';
import 'bar.ng_deps.dart' as i0; import 'bar.ng_deps.dart' as i5;
export 'index.dart'; export 'index.dart';
var _visited = false; var _visited = false;
void initReflector() { void initReflector() {
if (_visited) return; if (_visited) return;
_visited = true; _visited = true;
i0.initReflector(); i5.initReflector();
} }