feat(dart/transform): Inline templateUrl values

Modify DirectiveProcessor to inline `templateUrl` values to avoid making
additional browser requests.

Closes #1035
This commit is contained in:
Tim Blasi
2015-05-08 17:29:21 -07:00
parent 655ed851f0
commit 97d24563f4
13 changed files with 294 additions and 24 deletions

View File

@ -0,0 +1,106 @@
library angular2.test.transform.common.async_string_writer;
import 'dart:async';
import 'package:angular2/src/transform/common/async_string_writer.dart';
import 'package:guinness/guinness.dart';
void allTests() {
it('should function as a basic Writer without async calls.', () {
var writer = new AsyncStringWriter();
writer.print('hello');
expect('$writer').toEqual('hello');
writer.println(', world');
expect('$writer').toEqual('hello, world\n');
});
it('should concatenate futures added with `asyncPrint`.', () async {
var writer = new AsyncStringWriter();
writer.print('hello');
expect('$writer').toEqual('hello');
writer.asyncPrint(new Future.value(', world.'));
writer.print(' It is a beautiful day.');
expect(await writer.asyncToString())
.toEqual('hello, world. It is a beautiful day.');
});
it('should concatenate multiple futures regardless of order.', () async {
var completer1 = new Completer<String>();
var completer2 = new Completer<String>();
var writer = new AsyncStringWriter();
writer.print('hello');
expect('$writer').toEqual('hello');
writer.asyncPrint(completer1.future);
writer.asyncPrint(completer2.future);
completer2.complete(' It is a beautiful day.');
completer1.complete(', world.');
expect(await writer.asyncToString())
.toEqual('hello, world. It is a beautiful day.');
});
it('should allow multiple "rounds" of `asyncPrint`.', () async {
var writer = new AsyncStringWriter();
writer.print('hello');
expect('$writer').toEqual('hello');
writer.asyncPrint(new Future.value(', world.'));
expect(await writer.asyncToString()).toEqual('hello, world.');
writer.asyncPrint(new Future.value(' It is '));
writer.asyncPrint(new Future.value('a beautiful '));
writer.asyncPrint(new Future.value('day.'));
expect(await writer.asyncToString())
.toEqual('hello, world. It is a beautiful day.');
});
it('should handle calls to async methods while waiting.', () {
var completer1 = new Completer<String>();
var completer2 = new Completer<String>();
var writer = new AsyncStringWriter();
writer.print('hello');
expect('$writer').toEqual('hello');
writer.asyncPrint(completer1.future);
var f1 = writer.asyncToString().then((result) {
expect(result).toEqual('hello, world.');
});
writer.asyncPrint(completer2.future);
var f2 = writer.asyncToString().then((result) {
expect(result).toEqual('hello, world. It is a beautiful day.');
});
completer1.complete(', world.');
completer2.complete(' It is a beautiful day.');
return Future.wait([f1, f2]);
});
it('should handle calls to async methods that complete in reverse '
'order while waiting.', () {
var completer1 = new Completer<String>();
var completer2 = new Completer<String>();
var writer = new AsyncStringWriter();
writer.print('hello');
expect('$writer').toEqual('hello');
writer.asyncPrint(completer1.future);
var f1 = writer.asyncToString().then((result) {
expect(result).toEqual('hello, world.');
});
writer.asyncPrint(completer2.future);
var f2 = writer.asyncToString().then((result) {
expect(result).toEqual('hello, world. It is a beautiful day.');
});
completer2.complete(' It is a beautiful day.');
completer1.complete(', world.');
return Future.wait([f1, f2]);
});
}

View File

@ -19,15 +19,29 @@ String readFile(String path) {
}
class TestAssetReader implements AssetReader {
Future<String> readAsString(AssetId id, {Encoding encoding}) =>
new Future.value(readFile(id.path));
/// This allows "faking"
final Map<AssetId, String> _overrideAssets = <AssetId, String>{};
Future<String> readAsString(AssetId id, {Encoding encoding}) {
if (_overrideAssets.containsKey(id)) {
return new Future.value(_overrideAssets[id]);
} else {
return new Future.value(readFile(id.path));
}
}
Future<bool> hasInput(AssetId id) {
var exists = false;
var exists = _overrideAssets.containsKey(id);
if (exists) return new Future.value(true);
for (var myPath in [id.path, 'test/transform/${id.path}']) {
var file = new File(myPath);
exists = exists || file.existsSync();
}
return new Future.value(exists);
}
void addAsset(AssetId id, String contents) {
_overrideAssets[id] = contents;
}
}

View File

@ -3,10 +3,10 @@ library angular2.test.transform.directive_processor.all_tests;
import 'package:barback/barback.dart';
import 'package:angular2/src/transform/directive_processor/rewriter.dart';
import 'package:angular2/src/transform/common/annotation_matcher.dart';
import '../common/read_file.dart';
import 'package:dart_style/dart_style.dart';
import 'package:guinness/guinness.dart';
import 'package:path/path.dart' as path;
import '../common/read_file.dart';
var formatter = new DartFormatter();
@ -36,6 +36,9 @@ void allTests() {
customDescriptors: [
const AnnotationDescriptor('Soup', 'package:soup/soup.dart', 'Component'),
]);
_testNgDeps(
'should inline `templateUrl` values.', 'url_expression_files/hello.dart');
}
void _testNgDeps(String name, String inputPath,
@ -43,11 +46,13 @@ void _testNgDeps(String name, String inputPath,
it(name, () async {
var inputId = _assetIdForPath(inputPath);
var reader = new TestAssetReader();
var input = await reader.readAsString(inputId);
if (assetId != null) {
reader.addAsset(assetId, await reader.readAsString(inputId));
inputId = assetId;
}
var annotationMatcher = new AnnotationMatcher()..addAll(customDescriptors);
var proxyInputId = assetId != null ? assetId : inputId;
var output =
formatter.format(createNgDeps(input, proxyInputId, annotationMatcher));
var output = formatter
.format(await createNgDeps(reader, inputId, annotationMatcher));
var expectedPath = path.join(path.dirname(inputPath), 'expected',
path.basename(inputPath).replaceFirst('.dart', '.ng_deps.dart'));
var expectedId = _assetIdForPath(expectedPath);

View File

@ -0,0 +1,20 @@
library examples.src.hello_world.index_common_dart.ng_deps.dart;
import 'hello.dart';
import 'package:angular2/angular2.dart'
show bootstrap, Component, Directive, View, NgElement;
var _visited = false;
void initReflector(reflector) {
if (_visited) return;
_visited = true;
reflector
..registerType(HelloCmp, {
'factory': () => new HelloCmp(),
'parameters': const [],
'annotations': const [
const Component(selector: 'hello-app'),
const View(template: r'''{{greeting}}''')
]
});
}

View File

@ -0,0 +1,8 @@
library examples.src.hello_world.index_common_dart;
import 'package:angular2/angular2.dart'
show bootstrap, Component, Directive, View, NgElement;
@Component(selector: 'hello-app')
@View(templateUrl: 'template.html')
class HelloCmp {}

View File

@ -4,6 +4,7 @@ import 'package:guinness/guinness.dart';
import 'package:unittest/unittest.dart' hide expect;
import 'package:unittest/vm_config.dart';
import 'common/async_string_writer_tests.dart' as asyncStringWriter;
import 'bind_generator/all_tests.dart' as bindGenerator;
import 'directive_linker/all_tests.dart' as directiveLinker;
import 'directive_metadata_extractor/all_tests.dart' as directiveMeta;
@ -14,6 +15,7 @@ import 'template_compiler/all_tests.dart' as templateCompiler;
main() {
useVMConfiguration();
describe('AsyncStringWriter', asyncStringWriter.allTests);
describe('Bind Generator', bindGenerator.allTests);
describe('Directive Linker', directiveLinker.allTests);
describe('Directive Metadata Extractor', directiveMeta.allTests);