Make Angular2 transformers lazy in debug mode when option `lazy_transformers: true` is set.

This make apps to load quicker with pub serve (only builds what is
needed).
Note that lazy transformers seem to make pub build slower, so we wrap
transformers to force them to be eager in release mode.

Closes #5372
This commit is contained in:
Olivier Chafik 2015-11-18 15:07:08 -08:00 committed by Olivier Chafik
parent 5ba9ced1ab
commit 4e12b0831e
10 changed files with 115 additions and 14 deletions

View File

@ -0,0 +1,45 @@
library angular2.src.transform.common.eager_transformer_wrapper;
import 'package:barback/barback.dart';
abstract class EagerTransformerWrapper {
EagerTransformerWrapper._();
factory EagerTransformerWrapper(wrapped) {
return wrapped is AggregateTransformer
? new _EagerAggregateTransformerWrapper(wrapped)
: new _EagerTransformerWrapper(wrapped);
}
}
class _EagerTransformerWrapper extends EagerTransformerWrapper
implements Transformer {
final Transformer _wrapped;
_EagerTransformerWrapper(this._wrapped) : super._();
@override
String get allowedExtensions => _wrapped.allowedExtensions;
@override
apply(Transform transform) => _wrapped.apply(transform);
@override
isPrimary(AssetId id) => _wrapped.isPrimary(id);
@override
toString() => _wrapped.toString();
}
class _EagerAggregateTransformerWrapper extends EagerTransformerWrapper
implements AggregateTransformer {
final AggregateTransformer _wrapped;
_EagerAggregateTransformerWrapper(this._wrapped) : super._();
@override
apply(AggregateTransform transform) => _wrapped.apply(transform);
@override
classifyPrimary(AssetId id) => _wrapped.classifyPrimary(id);
@override
toString() => _wrapped.toString();
}

View File

@ -15,6 +15,7 @@ const PLATFORM_DIRECTIVES = 'platform_directives';
const INIT_REFLECTOR_PARAM = 'init_reflector'; const INIT_REFLECTOR_PARAM = 'init_reflector';
const INLINE_VIEWS_PARAM = 'inline_views'; const INLINE_VIEWS_PARAM = 'inline_views';
const MIRROR_MODE_PARAM = 'mirror_mode'; const MIRROR_MODE_PARAM = 'mirror_mode';
const LAZY_TRANSFORMERS = 'lazy_transformers';
/// Provides information necessary to transform an Angular2 app. /// Provides information necessary to transform an Angular2 app.
class TransformerOptions { class TransformerOptions {
@ -56,6 +57,13 @@ class TransformerOptions {
/// at any time. /// at any time.
final bool inlineViews; final bool inlineViews;
/// Whether to make transformers lazy.
/// If this is `true`, and in `debug` mode only, the transformers will be
/// lazy (will only build assets that are requested).
/// This is undocumented, for testing purposes only, and may change or break
/// at any time.
final bool lazyTransformers;
TransformerOptions._internal( TransformerOptions._internal(
this.entryPoints, this.entryPoints,
this.entryPointGlobs, this.entryPointGlobs,
@ -66,6 +74,7 @@ class TransformerOptions {
{this.reflectPropertiesAsAttributes, {this.reflectPropertiesAsAttributes,
this.platformDirectives, this.platformDirectives,
this.inlineViews, this.inlineViews,
this.lazyTransformers,
this.formatCode}); this.formatCode});
factory TransformerOptions(List<String> entryPoints, factory TransformerOptions(List<String> entryPoints,
@ -76,6 +85,7 @@ class TransformerOptions {
bool inlineViews: false, bool inlineViews: false,
bool reflectPropertiesAsAttributes: true, bool reflectPropertiesAsAttributes: true,
List<String> platformDirectives, List<String> platformDirectives,
bool lazyTransformers: false,
bool formatCode: false}) { bool formatCode: false}) {
var annotationMatcher = new AnnotationMatcher() var annotationMatcher = new AnnotationMatcher()
..addAll(customAnnotationDescriptors); ..addAll(customAnnotationDescriptors);
@ -87,6 +97,7 @@ class TransformerOptions {
reflectPropertiesAsAttributes: reflectPropertiesAsAttributes, reflectPropertiesAsAttributes: reflectPropertiesAsAttributes,
platformDirectives: platformDirectives, platformDirectives: platformDirectives,
inlineViews: inlineViews, inlineViews: inlineViews,
lazyTransformers: lazyTransformers,
formatCode: formatCode); formatCode: formatCode);
} }
} }

View File

@ -42,6 +42,8 @@ TransformerOptions parseBarbackSettings(BarbackSettings settings) {
reflectPropertiesAsAttributes: reflectPropertiesAsAttributes, reflectPropertiesAsAttributes: reflectPropertiesAsAttributes,
platformDirectives: platformDirectives, platformDirectives: platformDirectives,
inlineViews: _readBool(config, INLINE_VIEWS_PARAM, defaultValue: false), inlineViews: _readBool(config, INLINE_VIEWS_PARAM, defaultValue: false),
lazyTransformers:
_readBool(config, LAZY_TRANSFORMERS, defaultValue: false),
formatCode: formatCode); formatCode: formatCode);
} }

View File

@ -14,11 +14,16 @@ import 'rewriter.dart';
/// Transformer responsible for rewriting deferred library loads to enable /// Transformer responsible for rewriting deferred library loads to enable
/// initializing the reflector in a deferred way to keep the code with the /// initializing the reflector in a deferred way to keep the code with the
/// deferred library. /// deferred library.
class DeferredRewriter extends Transformer { class DeferredRewriter extends Transformer implements LazyTransformer {
final TransformerOptions options; final TransformerOptions options;
DeferredRewriter(this.options); DeferredRewriter(this.options);
@override
declareOutputs(DeclaringTransform transform) {
transform.declareOutput(transform.primaryId);
}
@override @override
bool isPrimary(AssetId id) => bool isPrimary(AssetId id) =>
id.extension.endsWith('dart') && _isNotGenerated(id); id.extension.endsWith('dart') && _isNotGenerated(id);

View File

@ -30,12 +30,17 @@ import 'ng_meta_linker.dart';
/// ///
/// This transformer is part of a multi-phase transform. /// This transformer is part of a multi-phase transform.
/// See `angular2/src/transform/transformer.dart` for transformer ordering. /// See `angular2/src/transform/transformer.dart` for transformer ordering.
class DirectiveMetadataLinker extends Transformer { class DirectiveMetadataLinker extends Transformer implements LazyTransformer {
final _encoder = const JsonEncoder.withIndent(' '); final _encoder = const JsonEncoder.withIndent(' ');
@override @override
bool isPrimary(AssetId id) => id.path.endsWith(SUMMARY_META_EXTENSION); bool isPrimary(AssetId id) => id.path.endsWith(SUMMARY_META_EXTENSION);
@override
declareOutputs(DeclaringTransform transform) {
transform.declareOutput(_ngLinkedAssetId(transform.primaryId));
}
@override @override
Future apply(Transform transform) { Future apply(Transform transform) {
return zone.exec(() { return zone.exec(() {
@ -45,13 +50,9 @@ class DirectiveMetadataLinker extends Transformer {
new AssetReader.fromTransform(transform), primaryId).then((ngMeta) { new AssetReader.fromTransform(transform), primaryId).then((ngMeta) {
if (ngMeta != null) { if (ngMeta != null) {
final outputId = _ngLinkedAssetId(primaryId); final outputId = _ngLinkedAssetId(primaryId);
if (!ngMeta.isEmpty) {
transform.addOutput(new Asset.fromString(
outputId, _encoder.convert(ngMeta.toJson())));
} else {
// Not outputting an asset could confuse barback. // Not outputting an asset could confuse barback.
transform.addOutput(new Asset.fromString(outputId, '')); var output = ngMeta.isEmpty ? '' : _encoder.convert(ngMeta.toJson());
} transform.addOutput(new Asset.fromString(outputId, output));
} }
}); });
}, log: transform.logger); }, log: transform.logger);

View File

@ -21,7 +21,7 @@ import 'rewriter.dart';
/// ///
/// This transformer is part of a multi-phase transform. /// This transformer is part of a multi-phase transform.
/// See `angular2/src/transform/transformer.dart` for transformer ordering. /// See `angular2/src/transform/transformer.dart` for transformer ordering.
class DirectiveProcessor extends Transformer { class DirectiveProcessor extends Transformer implements LazyTransformer {
final TransformerOptions options; final TransformerOptions options;
final _encoder = const JsonEncoder.withIndent(' '); final _encoder = const JsonEncoder.withIndent(' ');
@ -30,6 +30,11 @@ class DirectiveProcessor extends Transformer {
@override @override
bool isPrimary(AssetId id) => id.extension.endsWith('dart'); bool isPrimary(AssetId id) => id.extension.endsWith('dart');
@override
declareOutputs(DeclaringTransform transform) {
transform.declareOutput(_ngSummaryAssetId(transform.primaryId));
}
@override @override
Future apply(Transform transform) async { Future apply(Transform transform) async {
Html5LibDomAdapter.makeCurrent(); Html5LibDomAdapter.makeCurrent();

View File

@ -21,7 +21,7 @@ import 'remove_reflection_capabilities.dart';
/// have already been run and that a .ng_deps.dart file has been generated for /// have already been run and that a .ng_deps.dart file has been generated for
/// {@link options.entryPoint}. The instantiation of {@link ReflectionCapabilities} is /// {@link options.entryPoint}. The instantiation of {@link ReflectionCapabilities} is
/// replaced by calling `setupReflection` in that .ng_deps.dart file. /// replaced by calling `setupReflection` in that .ng_deps.dart file.
class ReflectionRemover extends Transformer { class ReflectionRemover extends Transformer implements LazyTransformer {
final TransformerOptions options; final TransformerOptions options;
ReflectionRemover(this.options); ReflectionRemover(this.options);
@ -30,6 +30,11 @@ class ReflectionRemover extends Transformer {
bool isPrimary(AssetId id) => options.entryPointGlobs != null && bool isPrimary(AssetId id) => options.entryPointGlobs != null &&
options.entryPointGlobs.any((g) => g.matches(id.path)); options.entryPointGlobs.any((g) => g.matches(id.path));
@override
declareOutputs(DeclaringTransform transform) {
transform.declareOutput(transform.primaryId);
}
@override @override
Future apply(Transform transform) async { Future apply(Transform transform) async {
return zone.exec(() async { return zone.exec(() async {

View File

@ -12,7 +12,7 @@ import 'package:angular2/src/transform/common/zone.dart' as zone;
import 'processor.dart'; import 'processor.dart';
/// Pre-compiles CSS stylesheet files to Dart code for Angular 2. /// Pre-compiles CSS stylesheet files to Dart code for Angular 2.
class StylesheetCompiler extends Transformer { class StylesheetCompiler extends Transformer implements LazyTransformer {
StylesheetCompiler(); StylesheetCompiler();
@override @override
@ -20,13 +20,29 @@ class StylesheetCompiler extends Transformer {
return id.path.endsWith(CSS_EXTENSION); return id.path.endsWith(CSS_EXTENSION);
} }
@override
declareOutputs(DeclaringTransform transform) {
// Note: we check this assumption below.
_getExpectedOutputs(transform.primaryId).forEach(transform.declareOutput);
}
List<AssetId> _getExpectedOutputs(AssetId cssId) =>
[shimmedStylesheetAssetId(cssId), nonShimmedStylesheetAssetId(cssId)];
@override @override
Future apply(Transform transform) async { Future apply(Transform transform) async {
final reader = new AssetReader.fromTransform(transform); final reader = new AssetReader.fromTransform(transform);
return zone.exec(() async { return zone.exec(() async {
Html5LibDomAdapter.makeCurrent(); Html5LibDomAdapter.makeCurrent();
var outputs = await processStylesheet(reader, transform.primaryInput.id); var primaryId = transform.primaryInput.id;
var outputs = await processStylesheet(reader, primaryId);
var expectedIds = _getExpectedOutputs(primaryId);
outputs.forEach((Asset compiledStylesheet) { outputs.forEach((Asset compiledStylesheet) {
var id = compiledStylesheet.id;
if (!expectedIds.contains(id)) {
throw new StateError(
'Unexpected output for css processing of $primaryId: $id');
}
transform.addOutput(compiledStylesheet); transform.addOutput(compiledStylesheet);
}); });
}, log: transform.logger); }, log: transform.logger);

View File

@ -23,7 +23,7 @@ import 'generator.dart';
/// ///
/// This transformer is part of a multi-phase transform. /// This transformer is part of a multi-phase transform.
/// See `angular2/src/transform/transformer.dart` for transformer ordering. /// See `angular2/src/transform/transformer.dart` for transformer ordering.
class TemplateCompiler extends Transformer { class TemplateCompiler extends Transformer implements LazyTransformer {
final TransformerOptions options; final TransformerOptions options;
TemplateCompiler(this.options); TemplateCompiler(this.options);
@ -31,6 +31,12 @@ class TemplateCompiler extends Transformer {
@override @override
bool isPrimary(AssetId id) => id.path.endsWith(META_EXTENSION); bool isPrimary(AssetId id) => id.path.endsWith(META_EXTENSION);
@override
declareOutputs(DeclaringTransform transform) {
transform.declareOutput(ngDepsAssetId(transform.primaryId));
transform.declareOutput(templatesAssetId(transform.primaryId));
}
@override @override
Future apply(Transform transform) async { Future apply(Transform transform) async {
return zone.exec(() async { return zone.exec(() async {

View File

@ -3,6 +3,7 @@ library angular2.src.transform.transformer;
import 'package:barback/barback.dart'; import 'package:barback/barback.dart';
import 'package:dart_style/dart_style.dart'; import 'package:dart_style/dart_style.dart';
import 'common/eager_transformer_wrapper.dart';
import 'common/formatter.dart' as formatter; import 'common/formatter.dart' as formatter;
import 'common/options.dart'; import 'common/options.dart';
import 'common/options_reader.dart'; import 'common/options_reader.dart';
@ -42,6 +43,10 @@ class AngularTransformerGroup extends TransformerGroup {
], ],
]; ];
} }
if (options.modeName == BarbackMode.RELEASE || !options.lazyTransformers) {
phases = phases
.map((phase) => phase.map((t) => new EagerTransformerWrapper(t)));
}
return new AngularTransformerGroup._(phases, return new AngularTransformerGroup._(phases,
formatCode: options.formatCode); formatCode: options.formatCode);
} }