@@ -6,6 +6,7 @@ import 'dart:async';
6
6
import 'dart:collection' ;
7
7
8
8
import 'package:stack_trace/stack_trace.dart' ;
9
+ import 'package:stream_transform/stream_transform.dart' ;
9
10
import 'package:watcher/watcher.dart' ;
10
11
11
12
import '../exception.dart' ;
@@ -104,7 +105,7 @@ class _Watcher {
104
105
/// Returns a future that will only complete if an unexpected error occurs.
105
106
Future watch (MultiDirWatcher watcher) async {
106
107
loop:
107
- await for (var event in watcher.events) {
108
+ await for (var event in _debounceEvents ( watcher.events) ) {
108
109
var extension = p.extension (event.path);
109
110
if (extension != '.sass' && extension != '.scss' ) continue ;
110
111
var url = p.toUri (p.canonicalize (event.path));
@@ -148,6 +149,31 @@ class _Watcher {
148
149
}
149
150
}
150
151
152
+ /// Combine [WatchEvent] s that happen in quick succession.
153
+ ///
154
+ /// Otherwise, if a file is erased and then rewritten, we can end up reading
155
+ /// the intermediate erased version.
156
+ Stream <WatchEvent > _debounceEvents (Stream <WatchEvent > events) {
157
+ return events
158
+ .transform (debounceBuffer (new Duration (milliseconds: 25 )))
159
+ .expand ((buffer) {
160
+ var typeForPath = new PathMap <ChangeType >();
161
+ for (var event in buffer) {
162
+ var oldType = typeForPath[event.path];
163
+ if (oldType == null ) {
164
+ typeForPath[event.path] = event.type;
165
+ } else if (event.type == ChangeType .REMOVE ) {
166
+ typeForPath[event.path] = ChangeType .REMOVE ;
167
+ } else if (oldType != ChangeType .ADD ) {
168
+ typeForPath[event.path] = ChangeType .MODIFY ;
169
+ }
170
+ }
171
+
172
+ return typeForPath.keys
173
+ .map ((path) => new WatchEvent (typeForPath[path], path));
174
+ });
175
+ }
176
+
151
177
/// Recompiles [nodes] and everything that transitively imports them, if
152
178
/// necessary.
153
179
Future _recompileDownstream (Iterable <StylesheetNode > nodes) async {
0 commit comments