2
2
// MIT-style license that can be found in the LICENSE file or at
3
3
// https://opensource.org/licenses/MIT.
4
4
5
+ import 'dart:async' ;
5
6
import 'dart:collection' ;
6
7
8
+ import 'package:async/async.dart' ;
7
9
import 'package:path/path.dart' as p;
8
10
import 'package:stack_trace/stack_trace.dart' ;
9
11
import 'package:stream_transform/stream_transform.dart' ;
@@ -19,41 +21,46 @@ import 'compile_stylesheet.dart';
19
21
import 'options.dart' ;
20
22
21
23
/// Watches all the files in [graph] for changes and updates them as necessary.
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);
35
- }
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 ;
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
+ }
52
59
}
53
- }
54
60
55
- print ("Sass is watching for changes. Press Ctrl-C to stop.\n " );
56
- await watcher.watch (dirWatcher);
61
+ print ("Sass is watching for changes. Press Ctrl-C to stop.\n " );
62
+ return watcher.watch (dirWatcher);
63
+ }());
57
64
}
58
65
59
66
/// Holds state that's shared across functions that react to changes on the
@@ -124,31 +131,39 @@ class _Watcher {
124
131
125
132
/// Listens to `watcher.events` and updates the filesystem accordingly.
126
133
///
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
- }
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
+ }
134
148
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 ;
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
+ }
150
165
}
151
- }
166
+ }(), onCancel : () => subscription ? . cancel ());
152
167
}
153
168
154
169
/// Handles a modify event for the stylesheet at [path] .
0 commit comments