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:
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user