Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use node_interop rather than manually declaring Node APIs #1127

Merged
merged 1 commit into from
Oct 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
134 changes: 34 additions & 100 deletions lib/src/io/node.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,63 +7,16 @@ import 'dart:convert';
import 'dart:js_util';

import 'package:js/js.dart';
import 'package:node_interop/fs.dart';
import 'package:node_interop/node_interop.dart';
import 'package:node_interop/stream.dart';
import 'package:path/path.dart' as p;
import 'package:source_span/source_span.dart';
import 'package:watcher/watcher.dart';

import '../exception.dart';
import '../node/chokidar.dart';

@JS()
class _FS {
external Object readFileSync(String path, [String encoding]);
external void writeFileSync(String path, String data);
external bool existsSync(String path);
external void mkdirSync(String path);
external _Stat statSync(String path);
external void unlinkSync(String path);
external List<Object> readdirSync(String path);
}

@JS()
class _Stat {
external bool isFile();
external bool isDirectory();
external _Date get mtime;
}

@JS()
class _Date {
external int getTime();
}

@JS()
class _Stderr {
external void write(String text);
}

@JS()
class _Stdin {
external String read();

external void on(String event, void callback([Object object]));
}

@JS()
class _SystemError {
external String get message;
external String get code;
external String get syscall;
external String get path;
}

@JS()
class _Process {
external String get platform;
external Object get env;
external String cwd();
}

class FileSystemException {
final String message;
final String path;
Expand All @@ -74,7 +27,7 @@ class FileSystemException {
}

class Stderr {
final _Stderr _stderr;
final Writable _stderr;

Stderr(this._stderr);

Expand All @@ -87,12 +40,6 @@ class Stderr {
void flush() {}
}

@JS("fs")
external _FS get _fs;

@JS("process")
external _Process get _process;

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
Expand All @@ -112,13 +59,13 @@ String readFile(String path) {

/// Wraps `fs.readFileSync` to throw a [FileSystemException].
Object _readFile(String path, [String encoding]) =>
_systemErrorToFileSystemException(() => _fs.readFileSync(path, encoding));
_systemErrorToFileSystemException(() => fs.readFileSync(path, encoding));

void writeFile(String path, String contents) =>
_systemErrorToFileSystemException(() => _fs.writeFileSync(path, contents));
_systemErrorToFileSystemException(() => fs.writeFileSync(path, contents));

void deleteFile(String path) =>
_systemErrorToFileSystemException(() => _fs.unlinkSync(path));
_systemErrorToFileSystemException(() => fs.unlinkSync(path));

Future<String> readStdin() async {
var completer = Completer<String>();
Expand All @@ -129,16 +76,16 @@ Future<String> readStdin() async {
});
// Node defaults all buffers to 'utf8'.
var sink = utf8.decoder.startChunkedConversion(innerSink);
_stdin.on('data', allowInterop(([chunk]) {
process.stdin.on('data', allowInterop(([Object chunk]) {
assert(chunk != null);
sink.add(chunk as List<int>);
}));
_stdin.on('end', allowInterop(([_]) {
process.stdin.on('end', allowInterop(([Object _]) {
// Callback for 'end' receives no args.
assert(_ == null);
sink.close();
}));
_stdin.on('error', allowInterop(([e]) {
process.stdin.on('error', allowInterop(([Object e]) {
assert(e != null);
stderr.writeln('Failed to read from stdin');
stderr.writeln(e);
Expand All @@ -148,7 +95,7 @@ Future<String> readStdin() async {
}

/// Cleans up a Node system error's message.
String _cleanErrorMessage(_SystemError error) {
String _cleanErrorMessage(JsSystemError error) {
// The error message is of the form "$code: $text, $syscall '$path'". We just
// want the text.
return error.message.substring("${error.code}: ".length,
Expand All @@ -161,12 +108,12 @@ bool fileExists(String path) {
// whether the entity in question is a file or a directory. Since false
// negatives are much more common than false positives, it works out in our
// favor to check this first.
if (!_fs.existsSync(path)) return false;
if (!fs.existsSync(path)) return false;

try {
return _fs.statSync(path).isFile();
return fs.statSync(path).isFile();
} catch (error) {
var systemError = error as _SystemError;
var systemError = error as JsSystemError;
if (systemError.code == 'ENOENT') return false;
rethrow;
}
Expand All @@ -179,12 +126,12 @@ bool dirExists(String path) {
// whether the entity in question is a file or a directory. Since false
// negatives are much more common than false positives, it works out in our
// favor to check this first.
if (!_fs.existsSync(path)) return false;
if (!fs.existsSync(path)) return false;

try {
return _fs.statSync(path).isDirectory();
return fs.statSync(path).isDirectory();
} catch (error) {
var systemError = error as _SystemError;
var systemError = error as JsSystemError;
if (systemError.code == 'ENOENT') return false;
rethrow;
}
Expand All @@ -194,27 +141,27 @@ bool dirExists(String path) {
void ensureDir(String path) {
return _systemErrorToFileSystemException(() {
try {
_fs.mkdirSync(path);
fs.mkdirSync(path);
} catch (error) {
var systemError = error as _SystemError;
var systemError = error as JsSystemError;
if (systemError.code == 'EEXIST') return;
if (systemError.code != 'ENOENT') rethrow;
ensureDir(p.dirname(path));
_fs.mkdirSync(path);
fs.mkdirSync(path);
}
});
}

Iterable<String> listDir(String path, {bool recursive = false}) {
return _systemErrorToFileSystemException(() {
if (!recursive) {
return _fs
return fs
.readdirSync(path)
.map((child) => p.join(path, child as String))
.where((child) => !dirExists(child));
} else {
Iterable<String> list(String parent) =>
_fs.readdirSync(parent).expand((child) {
fs.readdirSync(parent).expand((child) {
var path = p.join(parent, child as String);
return dirExists(path) ? list(path) : [path];
});
Expand All @@ -225,55 +172,42 @@ Iterable<String> listDir(String path, {bool recursive = false}) {
}

DateTime modificationTime(String path) =>
_systemErrorToFileSystemException(() => DateTime.fromMillisecondsSinceEpoch(
_fs.statSync(path).mtime.getTime()));
_systemErrorToFileSystemException(() =>
DateTime.fromMillisecondsSinceEpoch(fs.statSync(path).mtime.getTime()));

String getEnvironmentVariable(String name) =>
getProperty(_process.env, name) as String;
getProperty(process.env, name) as String;

/// Runs callback and converts any [_SystemError]s it throws into
/// Runs callback and converts any [JsSystemError]s it throws into
/// [FileSystemException]s.
T _systemErrorToFileSystemException<T>(T callback()) {
try {
return callback();
} catch (error) {
var systemError = error as _SystemError;
var systemError = error as JsSystemError;
throw FileSystemException._(
_cleanErrorMessage(systemError), systemError.path);
}
}

@JS("process.stderr")
external _Stderr get _stderr;

final stderr = Stderr(_stderr);
final stderr = Stderr(process.stderr);

@JS("process.stdin")
external _Stdin get _stdin;
bool get hasTerminal => process.stdout.isTTY ?? false;

bool get hasTerminal => _hasTerminal ?? false;
bool get isWindows => process.platform == 'win32';

bool get isWindows => _process.platform == 'win32';

bool get isMacOS => _process.platform == 'darwin';
bool get isMacOS => process.platform == 'darwin';

bool get isNode => true;

// Node seems to support ANSI escapes on all terminals.
bool get supportsAnsiEscapes => hasTerminal;

String get currentPath => _process.cwd();

@JS("process.stdout.isTTY")
external bool get _hasTerminal;
String get currentPath => process.cwd();

@JS("process.exitCode")
external int get exitCode;
int get exitCode => process.exitCode;

// TODO(nweiz): remove this ignore when dart-lang/sdk#39250 is fixed.
// ignore: inference_failure_on_function_return_type
@JS("process.exitCode")
external set exitCode(int code);
set exitCode(int code) => process.exitCode = code;

Future<Stream<WatchEvent>> watchDir(String path, {bool poll = false}) {
var watcher = chokidar.watch(
Expand Down
18 changes: 9 additions & 9 deletions lib/src/node.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'dart:js_util';
import 'dart:typed_data';

import 'package:js/js.dart';
import 'package:node_interop/js.dart';
import 'package:path/path.dart' as p;
import 'package:tuple/tuple.dart';

Expand All @@ -17,7 +18,6 @@ import 'compile.dart';
import 'exception.dart';
import 'io.dart';
import 'importer/node.dart';
import 'node/error.dart';
import 'node/exports.dart';
import 'node/function.dart';
import 'node/render_context.dart';
Expand Down Expand Up @@ -65,13 +65,13 @@ void main() {
///
/// [render]: https://github.com/sass/node-sass#options
void _render(
RenderOptions options, void callback(JSError error, RenderResult result)) {
RenderOptions options, void callback(JsError error, RenderResult result)) {
if (options.fiber != null) {
options.fiber.call(allowInterop(() {
try {
callback(null, _renderSync(options));
} catch (error) {
callback(error as JSError, null);
callback(error as JsError, null);
}
return null;
})).run();
Expand Down Expand Up @@ -166,8 +166,8 @@ RenderResult _renderSync(RenderOptions options) {
throw "unreachable";
}

/// Converts an exception to a [JSError].
JSError _wrapException(Object exception) {
/// Converts an exception to a [JsError].
JsError _wrapException(Object exception) {
if (exception is SassException) {
return _newRenderError(exception.toString().replaceFirst("Error: ", ""),
line: exception.span.start.line + 1,
Expand All @@ -177,7 +177,7 @@ JSError _wrapException(Object exception) {
: p.fromUri(exception.span.sourceUrl),
status: 1);
} else {
return JSError(exception.toString());
return JsError(exception.toString());
}
}

Expand Down Expand Up @@ -388,11 +388,11 @@ bool _enableSourceMaps(RenderOptions options) =>
options.sourceMap is String ||
(isTruthy(options.sourceMap) && options.outFile != null);

/// Creates a [JSError] with the given fields added to it so it acts like a Node
/// Creates a [JsError] with the given fields added to it so it acts like a Node
/// Sass error.
JSError _newRenderError(String message,
JsError _newRenderError(String message,
{int line, int column, String file, int status}) {
var error = JSError(message);
var error = JsError(message);
setProperty(error, 'formatted', 'Error: $message');
if (line != null) setProperty(error, 'line', line);
if (column != null) setProperty(error, 'column', column);
Expand Down
11 changes: 0 additions & 11 deletions lib/src/node/error.dart

This file was deleted.

1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ dependencies:
cli_repl: ">=0.1.3 <0.3.0"
collection: "^1.8.0"
meta: "^1.1.7"
node_interop: "^1.1.0"
js: "^0.6.0"
package_resolver: "^1.0.0"
path: "^1.6.0"
Expand Down
9 changes: 0 additions & 9 deletions test/node_api/api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import 'package:js/js.dart';
import 'package:path/path.dart' as p;

export 'package:sass/src/node/error.dart';
export 'package:sass/src/node/importer_result.dart';
export 'package:sass/src/node/render_context.dart';
export 'package:sass/src/node/render_options.dart';
Expand All @@ -24,11 +23,6 @@ final sass = _requireSass(p.absolute("build/npm/sass.dart"));
/// The Fiber class.
final fiber = _requireFiber("fibers");

/// A `null` that's guaranteed to be represented by JavaScript's `undefined`
/// value, not by `null`.
@JS()
external Object get undefined;

/// A `null` that's guaranteed to be represented by JavaScript's `null` value,
/// not by `undefined`.
///
Expand All @@ -39,9 +33,6 @@ final Object jsNull = _eval("null");
@JS("eval")
external Object _eval(String js);

@JS("process.chdir")
external void chdir(String directory);

@JS("require")
external Sass _requireSass(String path);

Expand Down