feat(dart/transform): Bail early for files with no deferred libraries

In `DeferredRewriter` if we can determine that there are no `deferred`
libraries imported, bail early to avoid parsing the entire file.
This commit is contained in:
Tim Blasi 2015-11-12 11:21:59 -08:00
parent 8f91ff84c7
commit f80321fd26
2 changed files with 39 additions and 22 deletions

View File

@ -14,39 +14,54 @@ import 'package:quiver/iterables.dart' as it;
class Rewriter {
final AssetId _entryPoint;
final AssetReader _reader;
final _FindDeferredLibraries _visitor;
Rewriter(this._entryPoint, this._reader);
Rewriter(AssetId entryPoint, AssetReader reader)
: _entryPoint = entryPoint,
_reader = reader,
_visitor = new _FindDeferredLibraries(reader, entryPoint);
/// Rewrites the provided code by finding all the deferred library imports
/// and loadLibrary invocations. Then it removes any libraries that don't
/// require angular codegen. For the remaining libraries it rewrites the
/// import to the corresponding ng_dep file, and chains a future which
/// first initializes the library.
/// Rewrites `loadLibrary` calls to initialize libraries once loaded.
///
/// 1. Finds all the deferred library imports and loadLibrary invocations in
/// `_entryPoint`
/// 2. Removes any libraries that don't require angular codegen.
/// 3. For the remaining libraries, rewrites the import to the corresponding
/// `ng_deps.dart` file.
/// 4. Chains a future to the `loadLibrary` call which initializes the
/// library.
///
/// To the extent possible, this method does not change line numbers or
/// offsets in the provided code to facilitate debugging via source maps.
Future<String> rewrite() async {
var code = await _reader.readAsString(_entryPoint);
var node = parseCompilationUnit(code);
// If we can determine there are no deferred libraries, avoid parsing the
// entire file and bail early.
var onlyDirectives = parseDirectives(code, name: _entryPoint.path);
if (onlyDirectives == null) return null;
onlyDirectives.directives.accept(_visitor);
if (_visitor.deferredImports.isEmpty) return null;
var node = parseCompilationUnit(code, name: _entryPoint.path);
if (node == null) return null;
return logElapsedAsync(() async {
var visitor = new _FindDeferredLibraries(_reader, _entryPoint);
node.accept(visitor);
node.declarations.accept(_visitor);
// Look to see if we found any deferred libraries
if (!visitor.hasDeferredLibrariesToRewrite()) return null;
if (!_visitor.hasDeferredLibrariesToRewrite()) return null;
// Remove any libraries that don't need angular codegen.
await visitor.cull();
await _visitor.cull();
// Check again if there are any deferred libraries.
if (!visitor.hasDeferredLibrariesToRewrite()) return null;
if (!_visitor.hasDeferredLibrariesToRewrite()) return null;
var compare = (AstNode a, AstNode b) => a.offset - b.offset;
visitor.deferredImports.sort(compare);
visitor.loadLibraryInvocations.sort(compare);
_visitor.deferredImports.sort(compare);
_visitor.loadLibraryInvocations.sort(compare);
var buf = new StringBuffer();
var idx =
visitor.deferredImports.fold(0, (int lastIdx, ImportDirective node) {
_visitor.deferredImports.fold(0, (int lastIdx, ImportDirective node) {
buf.write(code.substring(lastIdx, node.offset));
var import = code.substring(node.offset, node.end);
@ -54,7 +69,7 @@ class Rewriter {
return node.end;
});
idx = visitor.loadLibraryInvocations.fold(idx,
idx = _visitor.loadLibraryInvocations.fold(idx,
(int lastIdx, MethodInvocation node) {
buf.write(code.substring(lastIdx, node.offset));
var value = node.realTarget as SimpleIdentifier;

View File

@ -44,13 +44,15 @@ void _testRewriteDeferredLibraries(String name, String inputPath) {
path.dirname(inputPath), 'expected', path.basename(inputPath));
var expectedId = _assetIdForPath(expectedPath);
var output = await rewriteDeferredLibraries(reader, inputId);
var input = await reader.readAsString(expectedId);
if (input == null) {
// Null input signals no output. Ensure that is true.
expect(output).toBeNull();
var actualOutput = await rewriteDeferredLibraries(reader, inputId);
var expectedOutput = await reader.readAsString(expectedId);
if (expectedOutput == null) {
// Null expectedOutput signals no output. Ensure that is true.
expect(actualOutput).toBeNull();
} else {
expect(formatter.format(output)).toEqual(formatter.format(input));
expect(actualOutput).toBeNotNull();
expect(formatter.format(actualOutput))
.toEqual(formatter.format(expectedOutput));
}
}, log: new RecordingLogger());
});