Tim Blasi 5d9b1e90dc refactor(dart/transform): Separate log & zone code
- Move zone-related code out of logger.dart and into zone.dart.
- Rename `logger` => `log`.
- Add the ability to specify a zone-local `TemplateCompiler`.
2015-11-05 10:24:36 -08:00

108 lines
3.9 KiB
Dart

library angular2.transform.directive_processor.inliner;
import 'dart:async';
import 'package:analyzer/analyzer.dart';
import 'package:analyzer/src/generated/ast.dart';
import 'package:angular2/src/transform/common/async_string_writer.dart';
import 'package:angular2/src/transform/common/logging.dart';
import 'package:code_transformers/assets.dart';
import 'package:angular2/src/transform/common/asset_reader.dart';
import 'package:barback/barback.dart' show AssetId;
import 'package:source_span/source_span.dart';
import 'package:path/path.dart' as path;
/// Reads the code at `assetId`, inlining any `part` directives in that code.
///
/// Returns `null` if the code represented by `assetId` is a `part`.
///
/// Order of `part`s is preserved. That is, if in the main library we have
/// ```
/// library main;
///
/// part 'lib1.dart'
/// part 'lib2.dart'
/// ```
/// The output will first have the contents of lib1 followed by the contents of
/// lib2.dart, followed by the original code in the library.
Future<String> inlineParts(AssetReader reader, AssetId assetId) async {
var code = await reader.readAsString(assetId);
var directivesVisitor = new _NgDepsDirectivesVisitor();
parseDirectives(code, name: assetId.path)
.directives
.accept(directivesVisitor);
// If this is part of another library, its contents will be processed by its
// parent, so it does not need its own `.ng_deps.dart` file.
if (directivesVisitor.isPart) return null;
return logElapsedAsync(() {
return _getAllDeclarations(reader, assetId, code, directivesVisitor);
}, operationName: 'inlineParts', assetId: assetId);
}
/// Processes `visitor.parts`, reading and appending their contents to the
/// original `code`.
Future<String> _getAllDeclarations(AssetReader reader, AssetId assetId,
String code, _NgDepsDirectivesVisitor visitor) {
if (visitor.parts.isEmpty) return new Future<String>.value(code);
var partsStart = visitor.parts.first.offset,
partsEnd = visitor.parts.last.end;
var asyncWriter = new AsyncStringWriter(code.substring(0, partsStart));
visitor.parts.forEach((partDirective) {
var uri = stringLiteralToString(partDirective.uri);
var partAssetId = uriToAssetId(assetId, uri, log, null /* span */,
errorOnAbsolute: false);
asyncWriter.asyncPrint(reader.readAsString(partAssetId).then((partCode) {
if (partCode == null || partCode.isEmpty) {
log.warning('Empty part at "${partDirective.uri}. Ignoring.',
asset: partAssetId);
return '';
}
// Remove any directives -- we just want declarations.
var parsedDirectives = parseDirectives(partCode, name: uri).directives;
return partCode.substring(parsedDirectives.last.end);
}).catchError((err, stackTrace) {
log.warning(
'Failed while reading part at ${partDirective.uri}. Ignoring.\n'
'Error: $err\n'
'Stack Trace: $stackTrace',
asset: partAssetId,
span: new SourceFile(code, url: path.basename(assetId.path))
.span(partDirective.offset, partDirective.end));
}));
});
asyncWriter.print(code.substring(partsEnd));
return asyncWriter.asyncToString();
}
/// Visitor responsible for reading the `part` files of the visited AST and
/// determining if it is a part of another library.
class _NgDepsDirectivesVisitor extends Object with SimpleAstVisitor<Object> {
/// Whether the file we are processing is a part, that is, whether we have
/// visited a `part of` directive.
bool _isPart = false;
final List<PartDirective> _parts = <PartDirective>[];
bool get isPart => _isPart;
/// In the order encountered in the source.
Iterable<PartDirective> get parts => _parts;
@override
Object visitPartDirective(PartDirective node) {
_parts.add(node);
return null;
}
@override
Object visitPartOfDirective(PartOfDirective node) {
_isPart = true;
return null;
}
}