feat(dart/transform): Remove unnecessary .ng_deps.dart files

Removes `.ng_deps.dart` files which

1. Do not register any `@Injectable` classes
2. Do not call `initReflector` on any other `.ng_deps.dart` files.

Closes #1929
This commit is contained in:
Tim Blasi
2015-05-22 14:15:18 -07:00
parent cda35101df
commit c065fb1422
15 changed files with 166 additions and 65 deletions

View File

@ -11,18 +11,56 @@ import 'package:barback/barback.dart';
import 'package:code_transformers/assets.dart';
import 'package:path/path.dart' as path;
/// Checks the `.ng_deps.dart` file represented by `entryPoint` and
/// determines whether it is necessary to the functioning of the Angular 2
/// Dart app.
///
/// An `.ng_deps.dart` 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.dart` 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 {
var parser = new Parser(reader);
NgDeps ngDeps = await parser.parse(entryPoint);
if (ngDeps.registeredTypes.isNotEmpty) return true;
// We do not register any @Injectables, do we call any dependencies?
var linkedDepsMap =
await _processNgImports(reader, entryPoint, _getSortedDeps(ngDeps));
return linkedDepsMap.isNotEmpty;
}
/// Modifies the `.ng_deps.dart` file represented by `entryPoint` to call its
/// dependencies associated `initReflector` methods.
///
/// 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:
///
/// ```
/// import 'dependency.ng_deps.dart' as i0;
/// ...
/// void setupReflection(reflector) {
/// ...
/// i0.initReflector(reflector);
/// }
/// ```
Future<String> linkNgDeps(AssetReader reader, AssetId entryPoint) async {
var parser = new Parser(reader);
NgDeps ngDeps = await parser.parse(entryPoint);
if (ngDeps == null) return null;
var allDeps = <UriBasedDirective>[]
..addAll(ngDeps.imports)
..addAll(ngDeps.exports)
..sort((a, b) => a.end.compareTo(b.end));
var allDeps = _getSortedDeps(ngDeps);
var linkedDepsMap = await _processNgImports(reader, entryPoint, allDeps);
if (linkedDepsMap.isEmpty) return ngDeps.code;
if (linkedDepsMap.isEmpty) {
// We are not calling `initReflector` on any other libraries.
return ngDeps.code;
}
var importBuf = new StringBuffer();
var declarationBuf = new StringBuffer();
@ -49,6 +87,15 @@ Future<String> linkNgDeps(AssetReader reader, AssetId entryPoint) async {
'${code.substring(declarationSeamIdx)}';
}
/// All `import`s and `export`s in `ngDeps` sorted by order of appearance in
/// the file.
List<UriBasedDirective> _getSortedDeps(NgDeps ngDeps) {
return <UriBasedDirective>[]
..addAll(ngDeps.imports)
..addAll(ngDeps.exports)
..sort((a, b) => a.end.compareTo(b.end));
}
String _toDepsUri(String importUri) =>
'${path.withoutExtension(importUri)}${DEPS_EXTENSION}';
@ -67,10 +114,8 @@ Future<Map<UriBasedDirective, String>> _processNgImports(AssetReader reader,
.where(_isNotDartDirective)
.map((UriBasedDirective directive) {
var ngDepsUri = _toDepsUri(stringLiteralToString(directive.uri));
var ngDepsAsset = uriToAssetId(entryPoint, ngDepsUri, logger,
null /*
span */
);
var spanArg = null;
var ngDepsAsset = uriToAssetId(entryPoint, ngDepsUri, logger, spanArg);
if (ngDepsAsset == entryPoint) return nullFuture;
return reader.hasInput(ngDepsAsset).then((hasInput) {
if (hasInput) {

View File

@ -29,8 +29,10 @@ class DirectiveLinker extends Transformer {
var assetId = transform.primaryInput.id;
var assetPath = assetId.path;
var transformedCode = await linkNgDeps(reader, assetId);
var formattedCode = formatter.format(transformedCode, uri: assetPath);
transform.addOutput(new Asset.fromString(assetId, formattedCode));
if (transformedCode != null) {
var formattedCode = formatter.format(transformedCode, uri: assetPath);
transform.addOutput(new Asset.fromString(assetId, formattedCode));
}
} catch (ex, stackTrace) {
log.logger.error('Linking ng directives failed.\n'
'Exception: $ex\n'
@ -39,3 +41,29 @@ class DirectiveLinker extends Transformer {
return null;
}
}
/// Transformer responsible for removing unnecessary `.ng_deps.dart` files
/// created by {@link DirectiveProcessor}.
class EmptyNgDepsRemover extends Transformer {
EmptyNgDepsRemover();
@override
bool isPrimary(AssetId id) => id.path.endsWith(DEPS_EXTENSION);
@override
Future apply(Transform transform) async {
log.init(transform);
try {
var reader = new AssetReader.fromTransform(transform);
if (!(await isNecessary(reader, transform.primaryInput.id))) {
transform.consumePrimary();
}
} catch (ex, stackTrace) {
log.logger.error('Removing unnecessary ng deps failed.\n'
'Exception: $ex\n'
'Stack Trace: $stackTrace');
}
return null;
}
}