Improve Node file load speed. (#110)

This commit is contained in:
Natalie Weizenbaum 2017-02-04 12:20:34 -08:00 committed by GitHub
parent ac939f5bfe
commit 231a85be38
8 changed files with 60 additions and 44 deletions

View File

@ -68,6 +68,8 @@
* Error out if a function is passed an unknown named parameter.
* Improve the speed of loading large files on Node.
* Don't consider browser-prefixed selector pseudos to be superselectors of
differently- or non-prefixed selector pseudos with the same base name.

View File

@ -5,8 +5,9 @@
import 'package:path/path.dart' as p;
import 'src/ast/sass.dart';
import 'src/exception.dart';
import 'src/io.dart';
import 'src/sync_package_resolver.dart';
import 'src/utils.dart';
import 'src/visitor/perform.dart';
import 'src/visitor/serialize.dart';
@ -23,7 +24,7 @@ import 'src/visitor/serialize.dart';
/// Finally throws a [SassException] if conversion fails.
String render(String path,
{bool color: false, SyncPackageResolver packageResolver}) {
var contents = readSassFile(path);
var contents = readFile(path);
var url = p.toUri(path);
var sassTree = p.extension(path) == '.sass'
? new Stylesheet.parseSass(contents, url: url, color: color)

View File

@ -106,7 +106,7 @@ Future<String> _loadVersion() async {
var libDir =
p.fromUri(await Isolate.resolvePackageUri(Uri.parse('package:sass/')));
var pubspec = readFileAsString(p.join(libDir, '..', 'pubspec.yaml'));
var pubspec = readFile(p.join(libDir, '..', 'pubspec.yaml'));
return pubspec
.split("\n")
.firstWhere((line) => line.startsWith('version: '))

View File

@ -17,7 +17,7 @@ class Stderr {
void flush() {}
}
/// An error thrown by [readFileAsBytes] and [readFileAsString].
/// An error thrown by [readFile].
class FileSystemException {
String get message => null;
}
@ -28,11 +28,11 @@ Stderr get stderr => null;
/// Returns whether or not stdout is connected to an interactive terminal.
bool get hasTerminal => false;
/// Reads the file at [path] as a list of bytes.
List<int> readFileAsBytes(String path) => null;
/// Reads the file at [path] as a UTF-8 encoded string.
String readFileAsString(String path) => null;
///
/// Throws a [FileSystemException] if reading fails, and a [SassException] if
/// the file isn't valid UTF-8.
String readFile(String path) => null;
/// Returns whether a file at [path] exists.
bool fileExists(String path) => null;

View File

@ -2,9 +2,11 @@
// MIT-style license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
import 'dart:typed_data';
import 'package:js/js.dart';
import 'package:path/path.dart' as p;
import 'package:source_span/source_span.dart';
import '../exception.dart';
@JS()
class _FS {
@ -51,14 +53,28 @@ external _FS _require(String name);
final _fs = _require("fs");
List<int> readFileAsBytes(String path) => _readFile(path) as Uint8List;
String readFile(String path) {
// TODO(nweiz): explicitly decode the bytes as UTF-8 like we do in the VM when
// it doesn't cause a substantial performance degradation for large files. See
// also dart-lang/sdk#25377.
var contents = _readFile(path, 'utf8') as String;
if (!contents.contains("<EFBFBD>")) return contents;
String readFileAsString(String path) => _readFile(path, 'utf8') as String;
var sourceFile = new SourceFile(contents, url: p.toUri(path));
for (var i = 0; i < contents.length; i++) {
if (contents.codeUnitAt(i) != 0xFFFD) continue;
throw new SassException(
"Invalid UTF-8.", sourceFile.location(i).pointSpan());
}
// This should be unreachable.
return contents;
}
/// Wraps `fs.readFileSync` to throw a [FileSystemException].
_readFile(String path, [String encoding]) {
try {
return _fs.readFileSync(path);
return _fs.readFileSync(path, encoding);
} catch (error) {
throw new FileSystemException._(_cleanErrorMessage(error as _SystemError));
}

View File

@ -2,17 +2,42 @@
// MIT-style license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
import 'dart:convert';
import 'dart:io' as io;
import 'package:path/path.dart' as p;
import 'package:source_span/source_span.dart';
import '../exception.dart';
export 'dart:io' show exitCode, FileSystemException;
io.Stdout get stderr => io.stderr;
bool get hasTerminal => io.stdout.hasTerminal;
List<int> readFileAsBytes(String path) => new io.File(path).readAsBytesSync();
String readFile(String path) {
var bytes = new io.File(path).readAsBytesSync();
String readFileAsString(String path) => new io.File(path).readAsStringSync();
try {
return UTF8.decode(bytes);
} on FormatException {
var decoded = UTF8.decode(bytes, allowMalformed: true);
var sourceFile = new SourceFile(decoded, url: p.toUri(path));
// TODO(nweiz): Use [FormatException.offset] instead when
// dart-lang/sdk#28293 is fixed.
for (var i = 0; i < bytes.length; i++) {
if (decoded.codeUnitAt(i) != 0xFFFD) continue;
throw new SassException(
"Invalid UTF-8.", sourceFile.location(i).pointSpan());
}
// This should be unreachable, but we'll rethrow the original exception just
// in case.
rethrow;
}
}
bool fileExists(String path) => new io.File(path).existsSync();

View File

@ -3,16 +3,13 @@
// https://opensource.org/licenses/MIT.
import 'dart:collection';
import 'dart:convert';
import 'dart:math' as math;
import 'package:charcode/charcode.dart';
import 'package:collection/collection.dart';
import 'package:path/path.dart' as p;
import 'package:source_span/source_span.dart';
import 'ast/node.dart';
import 'exception.dart';
import 'io.dart';
import 'util/character.dart';
@ -257,31 +254,6 @@ List/*<T>*/ longestCommonSubsequence/*<T>*/(
}
}
/// Reads a Sass source file from diskx, and throws a [SassException] if UTF-8
/// decoding fails.
String readSassFile(String path) {
var bytes = readFileAsBytes(path);
try {
return UTF8.decode(bytes);
} on FormatException {
var decoded = UTF8.decode(bytes, allowMalformed: true);
var sourceFile = new SourceFile(decoded, url: p.toUri(path));
// TODO(nweiz): Use [FormatException.offset] instead when
// dart-lang/sdk#28293 is fixed.
for (var i = 0; i < bytes.length; i++) {
if (decoded.codeUnitAt(i) != 0xFFFD) continue;
throw new SassException(
"Invalid UTF-8.", sourceFile.location(i).pointSpan());
}
// This should be unreachable, but we'll rethrow the original exception just
// in case.
rethrow;
}
}
/// Prints a warning to standard error, associated with [span].
///
/// If [color] is `true`, this uses terminal colors.

View File

@ -636,7 +636,7 @@ class _PerformVisitor
return _importedFiles.putIfAbsent(path, () {
String contents;
try {
contents = readSassFile(path);
contents = readFile(path);
} on SassException catch (error) {
var frames = _stack.toList()..add(_stackFrame(import.span));
throw new SassRuntimeException(