Skip to content

Commit 753c17e

Browse files
committed
Revert "Abort sass if stdin is closed when watching (#1411)"
This reverts commit c7ab426. See #1665, #1411
1 parent fa0d2fb commit 753c17e

File tree

10 files changed

+73
-121
lines changed

10 files changed

+73
-121
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
## 1.52.1
2+
3+
### Command Line Interface
4+
5+
* Fix a bug where `--watch` mode would close immediately in TTY mode. This was
6+
caused by our change to close `--watch` when stdin was closed *outside of* TTY
7+
mode, which has been reverted for now while we work on a fix.
8+
19
## 1.52.0
210

311
* Add support for arbitrary modifiers at the end of plain CSS imports, in

bin/sass.dart

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
import 'dart:isolate';
66

7-
import 'package:async/async.dart';
87
import 'package:path/path.dart' as p;
98
import 'package:stack_trace/stack_trace.dart';
109
import 'package:term_glyph/term_glyph.dart' as term_glyph;
@@ -56,8 +55,7 @@ Future<void> main(List<String> args) async {
5655
var graph = StylesheetGraph(
5756
ImportCache(loadPaths: options.loadPaths, logger: options.logger));
5857
if (options.watch) {
59-
await CancelableOperation.race([onStdinClose(), watch(options, graph)])
60-
.valueOrCancellation();
58+
await watch(options, graph);
6159
return;
6260
}
6361

lib/src/executable/watch.dart

Lines changed: 56 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@
22
// MIT-style license that can be found in the LICENSE file or at
33
// https://opensource.org/licenses/MIT.
44

5-
import 'dart:async';
65
import 'dart:collection';
76

8-
import 'package:async/async.dart';
97
import 'package:path/path.dart' as p;
108
import 'package:stack_trace/stack_trace.dart';
119
import 'package:stream_transform/stream_transform.dart';
@@ -21,46 +19,41 @@ import 'compile_stylesheet.dart';
2119
import 'options.dart';
2220

2321
/// Watches all the files in [graph] for changes and updates them as necessary.
24-
///z
25-
/// Canceling the operation closes the watcher.
26-
CancelableOperation<void> watch(
27-
ExecutableOptions options, StylesheetGraph graph) {
28-
return unwrapCancelableOperation(() async {
29-
var directoriesToWatch = [
30-
..._sourceDirectoriesToDestinations(options).keys,
31-
for (var dir in _sourcesToDestinations(options).keys) p.dirname(dir),
32-
...options.loadPaths
33-
];
34-
35-
var dirWatcher = MultiDirWatcher(poll: options.poll);
36-
await Future.wait(directoriesToWatch.map((dir) {
37-
// If a directory doesn't exist, watch its parent directory so that we're
38-
// notified once it starts existing.
39-
while (!dirExists(dir)) {
40-
dir = p.dirname(dir);
41-
}
42-
return dirWatcher.watch(dir);
43-
}));
44-
45-
// Before we start paying attention to changes, compile all the stylesheets as
46-
// they currently exist. This ensures that changes that come in update a
47-
// known-good state.
48-
var watcher = _Watcher(options, graph);
49-
for (var entry in _sourcesToDestinations(options).entries) {
50-
graph.addCanonical(FilesystemImporter('.'),
51-
p.toUri(canonicalize(entry.key)), p.toUri(entry.key),
52-
recanonicalize: false);
53-
var success =
54-
await watcher.compile(entry.key, entry.value, ifModified: true);
55-
if (!success && options.stopOnError) {
56-
dirWatcher.events.listen(null).cancel();
57-
return CancelableOperation.fromFuture(Future<void>.value());
58-
}
22+
Future<void> watch(ExecutableOptions options, StylesheetGraph graph) async {
23+
var directoriesToWatch = [
24+
..._sourceDirectoriesToDestinations(options).keys,
25+
for (var dir in _sourcesToDestinations(options).keys) p.dirname(dir),
26+
...options.loadPaths
27+
];
28+
29+
var dirWatcher = MultiDirWatcher(poll: options.poll);
30+
await Future.wait(directoriesToWatch.map((dir) {
31+
// If a directory doesn't exist, watch its parent directory so that we're
32+
// notified once it starts existing.
33+
while (!dirExists(dir)) {
34+
dir = p.dirname(dir);
5935
}
36+
return dirWatcher.watch(dir);
37+
}));
38+
39+
// Before we start paying attention to changes, compile all the stylesheets as
40+
// they currently exist. This ensures that changes that come in update a
41+
// known-good state.
42+
var watcher = _Watcher(options, graph);
43+
for (var entry in _sourcesToDestinations(options).entries) {
44+
graph.addCanonical(FilesystemImporter('.'),
45+
p.toUri(canonicalize(entry.key)), p.toUri(entry.key),
46+
recanonicalize: false);
47+
var success =
48+
await watcher.compile(entry.key, entry.value, ifModified: true);
49+
if (!success && options.stopOnError) {
50+
dirWatcher.events.listen(null).cancel();
51+
return;
52+
}
53+
}
6054

61-
print("Sass is watching for changes. Press Ctrl-C to stop.\n");
62-
return watcher.watch(dirWatcher);
63-
}());
55+
print("Sass is watching for changes. Press Ctrl-C to stop.\n");
56+
await watcher.watch(dirWatcher);
6457
}
6558

6659
/// Holds state that's shared across functions that react to changes on the
@@ -131,39 +124,31 @@ class _Watcher {
131124

132125
/// Listens to `watcher.events` and updates the filesystem accordingly.
133126
///
134-
/// Returns an operation that will only complete if an unexpected error occurs
135-
/// (or if a complation error occurs and `--stop-on-error` is passed). This
136-
/// operation can be cancelled to close the watcher.
137-
CancelableOperation<void> watch(MultiDirWatcher watcher) {
138-
StreamSubscription<WatchEvent>? subscription;
139-
return CancelableOperation<void>.fromFuture(() async {
140-
subscription = _debounceEvents(watcher.events).listen(null);
141-
await for (var event in SubscriptionStream(subscription!)) {
142-
var extension = p.extension(event.path);
143-
if (extension != '.sass' &&
144-
extension != '.scss' &&
145-
extension != '.css') {
146-
continue;
147-
}
127+
/// Returns a future that will only complete if an unexpected error occurs.
128+
Future<void> watch(MultiDirWatcher watcher) async {
129+
await for (var event in _debounceEvents(watcher.events)) {
130+
var extension = p.extension(event.path);
131+
if (extension != '.sass' && extension != '.scss' && extension != '.css') {
132+
continue;
133+
}
148134

149-
switch (event.type) {
150-
case ChangeType.MODIFY:
151-
var success = await _handleModify(event.path);
152-
if (!success && _options.stopOnError) return;
153-
break;
154-
155-
case ChangeType.ADD:
156-
var success = await _handleAdd(event.path);
157-
if (!success && _options.stopOnError) return;
158-
break;
159-
160-
case ChangeType.REMOVE:
161-
var success = await _handleRemove(event.path);
162-
if (!success && _options.stopOnError) return;
163-
break;
164-
}
135+
switch (event.type) {
136+
case ChangeType.MODIFY:
137+
var success = await _handleModify(event.path);
138+
if (!success && _options.stopOnError) return;
139+
break;
140+
141+
case ChangeType.ADD:
142+
var success = await _handleAdd(event.path);
143+
if (!success && _options.stopOnError) return;
144+
break;
145+
146+
case ChangeType.REMOVE:
147+
var success = await _handleRemove(event.path);
148+
if (!success && _options.stopOnError) return;
149+
break;
165150
}
166-
}(), onCancel: () => subscription?.cancel());
151+
}
167152
}
168153

169154
/// Handles a modify event for the stylesheet at [path].

lib/src/io/interface.dart

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// MIT-style license that can be found in the LICENSE file or at
33
// https://opensource.org/licenses/MIT.
44

5-
import 'package:async/async.dart';
65
import 'package:watcher/watcher.dart';
76

87
/// An output sink that writes to this process's standard error.
@@ -95,15 +94,6 @@ String? getEnvironmentVariable(String name) => throw '';
9594
int get exitCode => throw '';
9695
set exitCode(int value) => throw '';
9796

98-
/// If stdin is a TTY, returns a [CancelableOperation] that completes once it
99-
/// closes.
100-
///
101-
/// Otherwise, returns a [CancelableOperation] that never completes.
102-
///
103-
/// As long as this is uncanceled, it will monopolize stdin so that nothing else
104-
/// can read from it.
105-
CancelableOperation<void> onStdinClose() => throw '';
106-
10797
/// Recursively watches the directory at [path] for modifications.
10898
///
10999
/// Returns a future that completes with a single-subscription stream once the

lib/src/io/node.dart

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import 'dart:async';
66
import 'dart:convert';
77
import 'dart:js_util';
88

9-
import 'package:async/async.dart';
109
import 'package:js/js.dart';
1110
import 'package:node_interop/fs.dart';
1211
import 'package:node_interop/node_interop.dart';
@@ -196,9 +195,6 @@ final stderr = Stderr(process.stderr);
196195
@JS('process.stdout.isTTY')
197196
external bool? get isTTY;
198197

199-
@JS('process.stdin.isTTY')
200-
external bool? get isStdinTTY;
201-
202198
bool get hasTerminal => isTTY == true;
203199

204200
bool get isWindows => process.platform == 'win32';
@@ -216,14 +212,6 @@ int get exitCode => process.exitCode;
216212

217213
set exitCode(int code) => process.exitCode = code;
218214

219-
CancelableOperation<void> onStdinClose() {
220-
var completer = CancelableCompleter<void>();
221-
if (isStdinTTY == true) {
222-
process.stdin.on('end', allowInterop(() => completer.complete()));
223-
}
224-
return completer.operation;
225-
}
226-
227215
Future<Stream<WatchEvent>> watchDir(String path, {bool poll = false}) {
228216
var watcher = chokidar.watch(
229217
path, ChokidarOptions(disableGlobbing: true, usePolling: poll));

lib/src/io/vm.dart

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,6 @@ DateTime modificationTime(String path) {
9090

9191
String? getEnvironmentVariable(String name) => io.Platform.environment[name];
9292

93-
CancelableOperation<void> onStdinClose() => io.stdin.hasTerminal
94-
? CancelableOperation.fromSubscription(io.stdin.listen(null))
95-
: CancelableCompleter<void>().operation;
96-
9793
Future<Stream<WatchEvent>> watchDir(String path, {bool poll = false}) async {
9894
var watcher = poll ? PollingDirectoryWatcher(path) : DirectoryWatcher(path);
9995

lib/src/utils.dart

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
import 'dart:math' as math;
66

7-
import 'package:async/async.dart';
87
import 'package:charcode/charcode.dart';
98
import 'package:collection/collection.dart';
109
import 'package:source_span/source_span.dart';
@@ -380,22 +379,6 @@ Future<V> putIfAbsentAsync<K, V>(
380379
return value;
381380
}
382381

383-
/// Unwraps a [Future] that wraps a [CancelableOperation].
384-
///
385-
/// If the returned operation is cancelled, it will cancel the inner operation
386-
/// as soon as the future completes.
387-
CancelableOperation<T> unwrapCancelableOperation<T>(
388-
Future<CancelableOperation<T>> future) {
389-
var completer = CancelableCompleter<T>(
390-
onCancel: () => future.then((operation) => operation.cancel()));
391-
392-
future.then((operation) {
393-
operation.then(completer.complete, onError: completer.completeError);
394-
}, onError: completer.completeError);
395-
396-
return completer.operation;
397-
}
398-
399382
/// Returns a deep copy of a map that contains maps.
400383
Map<K1, Map<K2, V>> copyMapOfMap<K1, K2, V>(Map<K1, Map<K2, V>> map) =>
401384
{for (var entry in map.entries) entry.key: Map.of(entry.value)};

pkg/sass_api/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 1.0.0-beta.46
2+
3+
* No user-visible changes.
4+
15
## 1.0.0-beta.45
26

37
* **Breaking change:** Replace `StaticImport.supports` and `StaticImport.media`

pkg/sass_api/pubspec.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ name: sass_api
22
# Note: Every time we add a new Sass AST node, we need to bump the *major*
33
# version because it's a breaking change for anyone who's implementing the
44
# visitor interface(s).
5-
version: 1.0.0-beta.45
5+
version: 1.0.0-beta.46
66
description: Additional APIs for Dart Sass.
77
homepage: https://github.com/sass/dart-sass
88

99
environment:
1010
sdk: '>=2.12.0 <3.0.0'
1111

1212
dependencies:
13-
sass: 1.52.0
13+
sass: 1.52.1
1414

1515
dev_dependencies:
1616
dartdoc: ^5.0.0

pubspec.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: sass
2-
version: 1.52.0
2+
version: 1.52.1
33
description: A Sass implementation in Dart.
44
homepage: https://github.com/sass/dart-sass
55

@@ -12,7 +12,7 @@ environment:
1212

1313
dependencies:
1414
args: ^2.0.0
15-
async: ^2.9.0
15+
async: ^2.5.0
1616
charcode: ^1.2.0
1717
cli_repl: ^0.2.1
1818
collection: ^1.15.0

0 commit comments

Comments
 (0)