@@ -41,7 +41,11 @@ Future watch(ExecutableOptions options, StylesheetGraph graph) async {
4141 var destination = options.sourcesToDestinations[source];
4242 graph.addCanonical (new FilesystemImporter ('.' ),
4343 p.toUri (p.canonicalize (source)), p.toUri (source));
44- await watcher.compile (source, destination, ifModified: true );
44+ var success = await watcher.compile (source, destination, ifModified: true );
45+ if (! success && options.stopOnError) {
46+ dirWatcher.events.listen (null ).cancel ();
47+ return ;
48+ }
4549 }
4650
4751 print ("Sass is watching for changes. Press Ctrl-C to stop.\n " );
@@ -51,25 +55,34 @@ Future watch(ExecutableOptions options, StylesheetGraph graph) async {
5155/// Holds state that's shared across functions that react to changes on the
5256/// filesystem.
5357class _Watcher {
58+ /// The options for the Sass executable.
5459 final ExecutableOptions _options;
5560
61+ /// The graph of stylesheets being compiled.
5662 final StylesheetGraph _graph;
5763
5864 _Watcher (this ._options, this ._graph);
5965
6066 /// Compiles the stylesheet at [source] to [destination] , and prints any
6167 /// errors that occur.
62- Future compile (String source, String destination,
68+ ///
69+ /// Returns whether or not compilation succeeded.
70+ Future <bool > compile (String source, String destination,
6371 {bool ifModified: false }) async {
6472 try {
6573 await compileStylesheet (_options, _graph, source, destination,
6674 ifModified: ifModified);
75+ return true ;
6776 } on SassException catch (error, stackTrace) {
6877 _delete (destination);
6978 _printError (error.toString (color: _options.color), stackTrace);
79+ exitCode = 65 ;
80+ return false ;
7081 } on FileSystemException catch (error, stackTrace) {
7182 _printError ("Error reading ${p .relative (error .path )}: ${error .message }." ,
7283 stackTrace);
84+ exitCode = 66 ;
85+ return false ;
7386 }
7487 }
7588
@@ -97,7 +110,7 @@ class _Watcher {
97110 stderr.writeln (new Trace .from (stackTrace).terse.toString ().trimRight ());
98111 }
99112
100- stderr.writeln ();
113+ if ( ! _options.stopOnError) stderr.writeln ();
101114 }
102115
103116 /// Listens to `watcher.events` and updates the filesystem accordingly.
@@ -119,31 +132,36 @@ class _Watcher {
119132 // from the graph.
120133 var node = _graph.nodes[url];
121134 _graph.reload (url);
122- await _recompileDownstream ([node]);
135+ var success = await _recompileDownstream ([node]);
136+ if (! success && _options.stopOnError) return ;
123137 break ;
124138
125139 case ChangeType .ADD :
126- await _retryPotentialImports (event.path);
140+ var success = await _retryPotentialImports (event.path);
141+ if (! success && _options.stopOnError) return ;
127142
128143 var destination = _destinationFor (event.path);
129144 if (destination == null ) continue loop;
130145
131146 _graph.addCanonical (
132147 new FilesystemImporter ('.' ), url, p.toUri (event.path));
133148
134- await compile (event.path, destination);
149+ success = await compile (event.path, destination);
150+ if (! success && _options.stopOnError) return ;
135151 break ;
136152
137153 case ChangeType .REMOVE :
138- await _retryPotentialImports (event.path);
154+ var success = await _retryPotentialImports (event.path);
155+ if (! success && _options.stopOnError) return ;
139156 if (! _graph.nodes.containsKey (url)) continue loop;
140157
141158 var destination = _destinationFor (event.path);
142159 if (destination != null ) _delete (destination);
143160
144161 var downstream = _graph.nodes[url].downstream;
145162 _graph.remove (url);
146- await _recompileDownstream (downstream);
163+ success = await _recompileDownstream (downstream);
164+ if (! success && _options.stopOnError) return ;
147165 break ;
148166 }
149167 }
@@ -176,29 +194,38 @@ class _Watcher {
176194
177195 /// Recompiles [nodes] and everything that transitively imports them, if
178196 /// necessary.
179- Future _recompileDownstream (Iterable <StylesheetNode > nodes) async {
197+ ///
198+ /// Returns whether all recompilations succeeded.
199+ Future <bool > _recompileDownstream (Iterable <StylesheetNode > nodes) async {
180200 var seen = new Set <StylesheetNode >();
181201 var toRecompile = new Queue .of (nodes);
182202
203+ var allSucceeded = true ;
183204 while (! toRecompile.isEmpty) {
184205 var node = toRecompile.removeFirst ();
185206 if (! seen.add (node)) continue ;
186207
187- await _compileIfEntrypoint (node.canonicalUrl);
208+ var success = await _compileIfEntrypoint (node.canonicalUrl);
209+ allSucceeded = allSucceeded && success;
210+ if (! success && _options.stopOnError) return false ;
211+
188212 toRecompile.addAll (node.downstream);
189213 }
214+ return allSucceeded;
190215 }
191216
192217 /// Compiles the stylesheet at [url] to CSS if it's an entrypoint that's being
193218 /// watched.
194- Future _compileIfEntrypoint (Uri url) async {
195- if (url.scheme != 'file' ) return ;
219+ ///
220+ /// Returns `false` if compilation failed, `true` otherwise.
221+ Future <bool > _compileIfEntrypoint (Uri url) async {
222+ if (url.scheme != 'file' ) return true ;
196223
197224 var source = p.fromUri (url);
198225 var destination = _destinationFor (source);
199- if (destination == null ) return ;
226+ if (destination == null ) return true ;
200227
201- await compile (source, destination);
228+ return await compile (source, destination);
202229 }
203230
204231 /// If a Sass file at [source] should be compiled to CSS, returns the path to
@@ -223,7 +250,9 @@ class _Watcher {
223250 /// Re-runs all imports in [_graph] that might refer to [path] , and recompiles
224251 /// the files that contain those imports if they end up importing new
225252 /// stylesheets.
226- Future _retryPotentialImports (String path) async {
253+ ///
254+ /// Returns whether all recompilations succeeded.
255+ Future <bool > _retryPotentialImports (String path) async {
227256 var name = _name (p.basename (path));
228257 var changed = < StylesheetNode > [];
229258 for (var node in _graph.nodes.values) {
@@ -250,7 +279,7 @@ class _Watcher {
250279 if (importChanged) changed.add (node);
251280 }
252281
253- await _recompileDownstream (changed);
282+ return await _recompileDownstream (changed);
254283 }
255284
256285 /// Removes an extension from [extension] , and a leading underscore if it has one.
0 commit comments