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:collection' ;
6
-
7
5
import 'package:path/path.dart' as p;
8
- import 'package:stack_trace/stack_trace.dart' ;
9
6
import 'package:stream_transform/stream_transform.dart' ;
10
7
import 'package:watcher/watcher.dart' ;
11
8
12
- import '../exception.dart' ;
13
9
import '../importer/filesystem.dart' ;
14
10
import '../io.dart' ;
15
11
import '../stylesheet_graph.dart' ;
16
12
import '../util/map.dart' ;
17
13
import '../util/multi_dir_watcher.dart' ;
18
- import '../utils.dart' ;
19
- import 'compile_stylesheet.dart' ;
14
+ import 'concurrent.dart' ;
20
15
import 'options.dart' ;
21
16
22
17
/// Watches all the files in [graph] for changes and updates them as necessary.
@@ -41,15 +36,17 @@ Future<void> watch(ExecutableOptions options, StylesheetGraph graph) async {
41
36
// they currently exist. This ensures that changes that come in update a
42
37
// known-good state.
43
38
var watcher = _Watcher (options, graph);
44
- for (var (source, destination) in _sourcesToDestinations (options).pairs) {
39
+ var sourcesToDestinations = _sourcesToDestinations (options);
40
+ for (var source in sourcesToDestinations.keys) {
45
41
graph.addCanonical (
46
42
FilesystemImporter ('.' ), p.toUri (canonicalize (source)), p.toUri (source),
47
43
recanonicalize: false );
48
- var success = await watcher.compile (source, destination, ifModified: true );
49
- if (! success && options.stopOnError) {
50
- dirWatcher.events.listen (null ).cancel ();
51
- return ;
52
- }
44
+ }
45
+ var success = await compileStylesheets (options, graph, sourcesToDestinations,
46
+ ifModified: true );
47
+ if (! success && options.stopOnError) {
48
+ dirWatcher.events.listen (null ).cancel ();
49
+ return ;
53
50
}
54
51
55
52
print ("Sass is watching for changes. Press Ctrl-C to stop.\n " );
@@ -67,34 +64,6 @@ final class _Watcher {
67
64
68
65
_Watcher (this ._options, this ._graph);
69
66
70
- /// Compiles the stylesheet at [source] to [destination] , and prints any
71
- /// errors that occur.
72
- ///
73
- /// Returns whether or not compilation succeeded.
74
- Future <bool > compile (String source, String destination,
75
- {bool ifModified = false }) async {
76
- try {
77
- await compileStylesheet (_options, _graph, source, destination,
78
- ifModified: ifModified);
79
- return true ;
80
- } on SassException catch (error, stackTrace) {
81
- if (! _options.emitErrorCss) _delete (destination);
82
- _printError (
83
- error.toString (color: _options.color), getTrace (error) ?? stackTrace);
84
- exitCode = 65 ;
85
- return false ;
86
- } on FileSystemException catch (error, stackTrace) {
87
- var path = error.path;
88
- _printError (
89
- path == null
90
- ? error.message
91
- : "Error reading ${p .relative (path )}: ${error .message }." ,
92
- getTrace (error) ?? stackTrace);
93
- exitCode = 66 ;
94
- return false ;
95
- }
96
- }
97
-
98
67
/// Deletes the file at [path] and prints a message about it.
99
68
void _delete (String path) {
100
69
try {
@@ -109,21 +78,6 @@ final class _Watcher {
109
78
}
110
79
}
111
80
112
- /// Prints [message] to standard error, with [stackTrace] if [_options.trace]
113
- /// is set.
114
- void _printError (String message, StackTrace stackTrace) {
115
- var buffer = StringBuffer (message);
116
-
117
- if (_options.trace) {
118
- buffer.writeln ();
119
- buffer.writeln ();
120
- buffer.write (Trace .from (stackTrace).terse.toString ().trimRight ());
121
- }
122
-
123
- if (! _options.stopOnError) buffer.writeln ();
124
- printError (buffer);
125
- }
126
-
127
81
/// Listens to `watcher.events` and updates the filesystem accordingly.
128
82
///
129
83
/// Returns a future that will only complete if an unexpected error occurs.
@@ -172,8 +126,9 @@ final class _Watcher {
172
126
/// Returns whether all necessary recompilations succeeded.
173
127
Future <bool > _handleAdd (String path) async {
174
128
var destination = _destinationFor (path);
175
-
176
- var success = destination == null || await compile (path, destination);
129
+ var success = destination == null ||
130
+ await compileStylesheets (_options, _graph, {path: destination},
131
+ ifModified: true );
177
132
var downstream = _graph.addCanonical (
178
133
FilesystemImporter ('.' ), _canonicalize (path), p.toUri (path));
179
134
return await _recompileDownstream (downstream) && success;
@@ -226,34 +181,42 @@ final class _Watcher {
226
181
/// Returns whether all recompilations succeeded.
227
182
Future <bool > _recompileDownstream (Iterable <StylesheetNode > nodes) async {
228
183
var seen = < StylesheetNode > {};
229
- var toRecompile = Queue .of (nodes);
230
-
231
184
var allSucceeded = true ;
232
- while (toRecompile.isNotEmpty) {
233
- var node = toRecompile.removeFirst ();
234
- if (! seen.add (node)) continue ;
185
+ while (nodes.isNotEmpty) {
186
+ nodes = [
187
+ for (var node in nodes)
188
+ if (seen.add (node)) node
189
+ ];
235
190
236
- var success = await _compileIfEntrypoint (node.canonicalUrl);
237
- allSucceeded = allSucceeded && success;
238
- if (! success && _options.stopOnError) return false ;
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 ;
239
197
240
- toRecompile.addAll (node.downstream);
198
+ allSucceeded = allSucceeded && success;
199
+ }
200
+
201
+ nodes = [for (var node in nodes) ...node.downstream];
241
202
}
242
203
return allSucceeded;
243
204
}
244
205
245
- /// Compiles the stylesheet at [url] to CSS if it's an entrypoint that's being
246
- /// watched.
247
- ///
248
- /// Returns `false` if compilation failed, `true` otherwise.
249
- Future <bool > _compileIfEntrypoint (Uri url) async {
250
- if (url.scheme != 'file' ) return true ;
251
-
252
- var source = p.fromUri (url);
253
- return switch (_destinationFor (source)) {
254
- var destination? => await compile (source, destination),
255
- _ => true
256
- };
206
+ /// Returns a sourcesToDestinations mapping for nodes that are entrypoints.
207
+ Map <String , String > _sourceEntrypointsToDestinations (
208
+ Iterable <StylesheetNode > nodes) {
209
+ var entrypoints = < String , String > {};
210
+ for (var node in nodes) {
211
+ var url = node.canonicalUrl;
212
+ if (url.scheme != 'file' ) continue ;
213
+
214
+ var source = p.fromUri (url);
215
+ if (_destinationFor (source) case var destination? ) {
216
+ entrypoints[source] = destination;
217
+ }
218
+ }
219
+ return entrypoints;
257
220
}
258
221
259
222
/// If a Sass file at [source] should be compiled to CSS, returns the path to
0 commit comments