@@ -62,6 +62,10 @@ final class _Watcher {
62
62
/// The graph of stylesheets being compiled.
63
63
final StylesheetGraph _graph;
64
64
65
+ /// A map from source paths to destinations that need to be recompiled once
66
+ /// the current batch of events has been processed.
67
+ final Map <String , String > _toRecompile = {};
68
+
65
69
_Watcher (this ._options, this ._graph);
66
70
67
71
/// Deletes the file at [path] and prints a message about it.
@@ -82,70 +86,75 @@ final class _Watcher {
82
86
///
83
87
/// Returns a future that will only complete if an unexpected error occurs.
84
88
Future <void > watch (MultiDirWatcher watcher) async {
85
- await for (var event in _debounceEvents (watcher.events)) {
86
- var extension = p.extension (event.path);
87
- if (extension != '.sass' && extension != '.scss' && extension != '.css' ) {
88
- continue ;
89
+ await for (var batch in _debounceEvents (watcher.events)) {
90
+ for (var event in batch) {
91
+ var extension = p.extension (event.path);
92
+ if (extension != '.sass' &&
93
+ extension != '.scss' &&
94
+ extension != '.css' ) {
95
+ continue ;
96
+ }
97
+
98
+ switch (event.type) {
99
+ case ChangeType .MODIFY :
100
+ _handleModify (event.path);
101
+
102
+ case ChangeType .ADD :
103
+ _handleAdd (event.path);
104
+
105
+ case ChangeType .REMOVE :
106
+ _handleRemove (event.path);
107
+ }
89
108
}
90
109
91
- switch (event.type) {
92
- case ChangeType .MODIFY :
93
- var success = await _handleModify (event.path);
94
- if (! success && _options.stopOnError) return ;
95
-
96
- case ChangeType .ADD :
97
- var success = await _handleAdd (event.path);
98
- if (! success && _options.stopOnError) return ;
99
-
100
- case ChangeType .REMOVE :
101
- var success = await _handleRemove (event.path);
102
- if (! success && _options.stopOnError) return ;
103
- }
110
+ var toRecompile = {..._toRecompile};
111
+ _toRecompile.clear ();
112
+ var success = await compileStylesheets (_options, _graph, toRecompile,
113
+ ifModified: true );
114
+ if (! success && _options.stopOnError) return ;
104
115
}
105
116
}
106
117
107
118
/// Handles a modify event for the stylesheet at [path] .
108
119
///
109
120
/// Returns whether all necessary recompilations succeeded.
110
- Future < bool > _handleModify (String path) async {
121
+ void _handleModify (String path) {
111
122
var url = _canonicalize (path);
112
123
113
124
// It's important to access the node ahead-of-time because it's possible
114
125
// that `_graph.reload()` notices the file has been deleted and removes it
115
126
// from the graph.
116
127
if (_graph.nodes[url] case var node? ) {
117
128
_graph.reload (url);
118
- return await _recompileDownstream ([node]);
129
+ _recompileDownstream ([node]);
119
130
} else {
120
- return _handleAdd (path);
131
+ _handleAdd (path);
121
132
}
122
133
}
123
134
124
135
/// Handles an add event for the stylesheet at [url] .
125
136
///
126
137
/// Returns whether all necessary recompilations succeeded.
127
- Future < bool > _handleAdd (String path) async {
138
+ void _handleAdd (String path) {
128
139
var destination = _destinationFor (path);
129
- var success = destination == null ||
130
- await compileStylesheets (_options, _graph, {path: destination},
131
- ifModified: true );
140
+ if (destination != null ) _toRecompile[path] = destination;
132
141
var downstream = _graph.addCanonical (
133
142
FilesystemImporter .cwd, _canonicalize (path), p.toUri (path));
134
- return await _recompileDownstream (downstream) && success ;
143
+ _recompileDownstream (downstream);
135
144
}
136
145
137
146
/// Handles a remove event for the stylesheet at [url] .
138
147
///
139
148
/// Returns whether all necessary recompilations succeeded.
140
- Future < bool > _handleRemove (String path) async {
149
+ void _handleRemove (String path) async {
141
150
var url = _canonicalize (path);
142
151
143
152
if (_graph.nodes.containsKey (url)) {
144
153
if (_destinationFor (path) case var destination? ) _delete (destination);
145
154
}
146
155
147
156
var downstream = _graph.remove (FilesystemImporter .cwd, url);
148
- return await _recompileDownstream (downstream);
157
+ _recompileDownstream (downstream);
149
158
}
150
159
151
160
/// Returns the canonical URL for the stylesheet path [path] .
@@ -154,9 +163,10 @@ final class _Watcher {
154
163
/// Combine [WatchEvent] s that happen in quick succession.
155
164
///
156
165
/// Otherwise, if a file is erased and then rewritten, we can end up reading
157
- /// the intermediate erased version.
158
- Stream <WatchEvent > _debounceEvents (Stream <WatchEvent > events) {
159
- return events.debounceBuffer (Duration (milliseconds: 25 )).expand ((buffer) {
166
+ /// the intermediate erased version. This returns a stream of batches of
167
+ /// events that all happened in succession.
168
+ Stream <List <WatchEvent >> _debounceEvents (Stream <WatchEvent > events) {
169
+ return events.debounceBuffer (Duration (milliseconds: 25 )).map ((buffer) {
160
170
var typeForPath = p.PathMap <ChangeType >();
161
171
for (var event in buffer) {
162
172
var oldType = typeForPath[event.path];
@@ -175,32 +185,20 @@ final class _Watcher {
175
185
});
176
186
}
177
187
178
- /// Recompiles [nodes] and everything that transitively imports them, if
179
- /// necessary.
180
- ///
181
- /// Returns whether all recompilations succeeded.
182
- Future <bool > _recompileDownstream (Iterable <StylesheetNode > nodes) async {
188
+ /// Marks [nodes] and everything that transitively imports them for
189
+ /// recompilation, if necessary.
190
+ void _recompileDownstream (Iterable <StylesheetNode > nodes) {
183
191
var seen = < StylesheetNode > {};
184
- var allSucceeded = true ;
185
192
while (nodes.isNotEmpty) {
186
193
nodes = [
187
194
for (var node in nodes)
188
195
if (seen.add (node)) node
189
196
];
190
197
191
- var sourcesToDestinations = _sourceEntrypointsToDestinations (nodes);
192
- if (sourcesToDestinations.isNotEmpty) {
193
- var success = await compileStylesheets (
194
- _options, _graph, sourcesToDestinations,
195
- ifModified: true );
196
- if (! success && _options.stopOnError) return false ;
197
-
198
- allSucceeded = allSucceeded && success;
199
- }
198
+ _toRecompile.addAll (_sourceEntrypointsToDestinations (nodes));
200
199
201
200
nodes = [for (var node in nodes) ...node.downstream];
202
201
}
203
- return allSucceeded;
204
202
}
205
203
206
204
/// Returns a sourcesToDestinations mapping for nodes that are entrypoints.
0 commit comments