From ec49bf84debc2c9bb0350d510165d91f034d3254 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=AA=E3=81=A4=E3=81=8D?= Date: Sat, 26 Oct 2024 09:29:47 -0700 Subject: [PATCH] Lazy load @parcel/watcher and fallback to chokidar --- lib/src/io/js.dart | 108 +++++++++++++++------------- lib/src/js/parcel_watcher.dart | 51 +++++++------ lib/src/parse/parser.dart | 2 +- lib/src/visitor/async_evaluate.dart | 2 +- lib/src/visitor/evaluate.dart | 4 +- pkg/sass_api/pubspec.yaml | 2 +- pubspec.yaml | 7 +- tool/grind.dart | 3 +- 8 files changed, 98 insertions(+), 81 deletions(-) diff --git a/lib/src/io/js.dart b/lib/src/io/js.dart index e8b2e7722..51aced88c 100644 --- a/lib/src/io/js.dart +++ b/lib/src/io/js.dart @@ -254,60 +254,68 @@ Future> watchDir(String path, {bool poll = false}) async { throw UnsupportedError("watchDir() is only supported on Node.js"); } + if (poll || parcelWatcher == null) { + return _chokidarWatchDir(path, poll: poll); + } else { + return _parcelWatcherWatchDir(path); + } +} + +Future> _chokidarWatchDir(String path, {bool poll = false}) { // Don't assign the controller until after the ready event fires. Otherwise, // Chokidar will give us a bunch of add events for files that already exist. StreamController? controller; - if (poll) { - var watcher = chokidar.watch(path, ChokidarOptions(usePolling: true)); - watcher - ..on( - 'add', - allowInterop((String path, [void _]) => - controller?.add(WatchEvent(ChangeType.ADD, path)))) - ..on( - 'change', - allowInterop((String path, [void _]) => - controller?.add(WatchEvent(ChangeType.MODIFY, path)))) - ..on( - 'unlink', - allowInterop((String path) => - controller?.add(WatchEvent(ChangeType.REMOVE, path)))) - ..on( - 'error', allowInterop((Object error) => controller?.addError(error))); - - var completer = Completer>(); - watcher.on('ready', allowInterop(() { - // dart-lang/sdk#45348 - var stream = (controller = StreamController(onCancel: () { - watcher.close(); - })) - .stream; - completer.complete(stream); - })); - - return completer.future; - } else { - var subscription = await ParcelWatcher.subscribeFuture(path, - (Object? error, List events) { - if (error != null) { - controller?.addError(error); - } else { - for (var event in events) { - switch (event.type) { - case 'create': - controller?.add(WatchEvent(ChangeType.ADD, event.path)); - case 'update': - controller?.add(WatchEvent(ChangeType.MODIFY, event.path)); - case 'delete': - controller?.add(WatchEvent(ChangeType.REMOVE, event.path)); - } + var watcher = chokidar.watch(path, ChokidarOptions(usePolling: poll)); + watcher + ..on( + 'add', + allowInterop((String path, [void _]) => + controller?.add(WatchEvent(ChangeType.ADD, path)))) + ..on( + 'change', + allowInterop((String path, [void _]) => + controller?.add(WatchEvent(ChangeType.MODIFY, path)))) + ..on( + 'unlink', + allowInterop((String path) => + controller?.add(WatchEvent(ChangeType.REMOVE, path)))) + ..on('error', allowInterop((Object error) => controller?.addError(error))); + + var completer = Completer>(); + watcher.on('ready', allowInterop(() { + // dart-lang/sdk#45348 + var stream = (controller = StreamController(onCancel: () { + watcher.close(); + })) + .stream; + completer.complete(stream); + })); + + return completer.future; +} + +Future> _parcelWatcherWatchDir(String path) async { + StreamController? controller; + var subscription = await parcelWatcher!.subscribe(path, + (Object? error, List events) { + if (error != null) { + controller?.addError(error); + } else { + for (var event in events) { + switch (event.type) { + case 'create': + controller?.add(WatchEvent(ChangeType.ADD, event.path)); + case 'update': + controller?.add(WatchEvent(ChangeType.MODIFY, event.path)); + case 'delete': + controller?.add(WatchEvent(ChangeType.REMOVE, event.path)); } } - }); + } + }); - return (controller = StreamController(onCancel: () { - subscription.unsubscribe(); - })) - .stream; - } + return (controller = StreamController(onCancel: () { + subscription.unsubscribe(); + })) + .stream; } diff --git a/lib/src/js/parcel_watcher.dart b/lib/src/js/parcel_watcher.dart index 1cf698a23..4d1720bdd 100644 --- a/lib/src/js/parcel_watcher.dart +++ b/lib/src/js/parcel_watcher.dart @@ -2,17 +2,15 @@ // MIT-style license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. -import 'package:js/js.dart'; -import 'package:node_interop/js.dart'; -import 'package:node_interop/util.dart'; +import 'dart:js_interop'; @JS() -class ParcelWatcherSubscription { +extension type ParcelWatcherSubscription(JSObject _) implements JSObject { external void unsubscribe(); } @JS() -class ParcelWatcherEvent { +extension type ParcelWatcherEvent(JSObject _) implements JSObject { external String get type; external String get path; } @@ -20,25 +18,32 @@ class ParcelWatcherEvent { /// The @parcel/watcher module. /// /// See [the docs on npm](https://www.npmjs.com/package/@parcel/watcher). -@JS('parcel_watcher') -class ParcelWatcher { - external static Promise subscribe(String path, Function callback); - static Future subscribeFuture(String path, +@JS() +extension type ParcelWatcher(JSObject _) implements JSObject { + @JS('subscribe') + external JSPromise _subscribe( + String path, JSFunction callback); + Future subscribe(String path, void Function(Object? error, List) callback) => - promiseToFuture( - subscribe(path, allowInterop((Object? error, List events) { - callback(error, events.cast()); - }))); + _subscribe( + path, + (JSObject? error, JSArray events) { + callback(error, events.toDart); + }.toJS) + .toDart; - external static Promise getEventsSince(String path, String snapshotPath); - static Future> getEventsSinceFuture( - String path, String snapshotPath) async { - List events = - await promiseToFuture(getEventsSince(path, snapshotPath)); - return events.cast(); - } + @JS('getEventsSince') + external JSPromise> _getEventsSince( + String path, String snapshotPath); + Future> getEventsSince( + String path, String snapshotPath) async => + (await _getEventsSince(path, snapshotPath).toDart).toDart; - external static Promise writeSnapshot(String path, String snapshotPath); - static Future writeSnapshotFuture(String path, String snapshotPath) => - promiseToFuture(writeSnapshot(path, snapshotPath)); + @JS('writeSnapshot') + external JSPromise _writeSnapshot(String path, String snapshotPath); + Future writeSnapshot(String path, String snapshotPath) => + _writeSnapshot(path, snapshotPath).toDart; } + +@JS('parcel_watcher') +external ParcelWatcher? get parcelWatcher; diff --git a/lib/src/parse/parser.dart b/lib/src/parse/parser.dart index 4c4d6ba5e..19ef3971c 100644 --- a/lib/src/parse/parser.dart +++ b/lib/src/parse/parser.dart @@ -651,7 +651,7 @@ class Parser { var span = scanner.spanFrom(state); return _interpolationMap == null ? span - : LazyFileSpan(() => _interpolationMap!.mapSpan(span)); + : LazyFileSpan(() => _interpolationMap.mapSpan(span)); } /// Throws an error associated with [span]. diff --git a/lib/src/visitor/async_evaluate.dart b/lib/src/visitor/async_evaluate.dart index 4dc806e67..aba5cee7c 100644 --- a/lib/src/visitor/async_evaluate.dart +++ b/lib/src/visitor/async_evaluate.dart @@ -1812,7 +1812,7 @@ final class _EvaluateVisitor if (result != null) { isDependency = _inDependency; } else { - result = await _nodeImporter!.loadAsync(originalUrl, previous, forImport); + result = await _nodeImporter.loadAsync(originalUrl, previous, forImport); if (result == null) return null; isDependency = true; } diff --git a/lib/src/visitor/evaluate.dart b/lib/src/visitor/evaluate.dart index e06601361..f9990c828 100644 --- a/lib/src/visitor/evaluate.dart +++ b/lib/src/visitor/evaluate.dart @@ -5,7 +5,7 @@ // DO NOT EDIT. This file was generated from async_evaluate.dart. // See tool/grind/synchronize.dart for details. // -// Checksum: 396c8f169d95c601598b8c3be1f4b948ca22effa +// Checksum: 3986f5db33dd220dcd971a39e8587ca4e52d9a3f // // ignore_for_file: unused_import @@ -1808,7 +1808,7 @@ final class _EvaluateVisitor if (result != null) { isDependency = _inDependency; } else { - result = _nodeImporter!.load(originalUrl, previous, forImport); + result = _nodeImporter.load(originalUrl, previous, forImport); if (result == null) return null; isDependency = true; } diff --git a/pkg/sass_api/pubspec.yaml b/pkg/sass_api/pubspec.yaml index 5d118d0ae..b7a1b2bb0 100644 --- a/pkg/sass_api/pubspec.yaml +++ b/pkg/sass_api/pubspec.yaml @@ -7,7 +7,7 @@ description: Additional APIs for Dart Sass. homepage: https://github.com/sass/dart-sass environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.3.0 <4.0.0" dependencies: sass: 1.80.5 diff --git a/pubspec.yaml b/pubspec.yaml index f7817a98b..6960fcd5e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -8,13 +8,16 @@ executables: sass: sass environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.3.0 <4.0.0" dependencies: args: ^2.0.0 async: ^2.5.0 charcode: ^1.2.0 - cli_pkg: ^2.8.0 + cli_pkg: + git: + url: https://github.com/google/dart_cli_pkg.git + ref: refs/pull/169/head cli_repl: ^0.2.1 collection: ^1.16.0 http: ^1.1.0 diff --git a/tool/grind.dart b/tool/grind.dart index cf64cc52e..6f2acc9db 100644 --- a/tool/grind.dart +++ b/tool/grind.dart @@ -35,7 +35,8 @@ void main(List args) { pkg.homebrewFormula.value = "Formula/sass.rb"; pkg.homebrewEditFormula.value = _updateHomebrewLanguageRevision; pkg.jsRequires.value = [ - pkg.JSRequire("@parcel/watcher", target: pkg.JSRequireTarget.cli), + pkg.JSRequire("@parcel/watcher", + target: pkg.JSRequireTarget.cli, lazy: true, optional: true), pkg.JSRequire("immutable", target: pkg.JSRequireTarget.all), pkg.JSRequire("chokidar", target: pkg.JSRequireTarget.cli), pkg.JSRequire("readline", target: pkg.JSRequireTarget.cli),