Skip to content

Commit da84fb5

Browse files
corebontsnex3
andauthored
Fix --watch not recompiling sources modified during a compilation (#2850)
Sources modified while a compilation is in progress are older than the output that compilation writes when it finishes, so --watch and --update wrongly consider them up to date and skip recompilation. Set the output's modification time to when compilation started so such sources compare as newer, including across restarts. --------- Co-authored-by: Natalie Weizenbaum <nweiz@google.com>
1 parent 8a287a7 commit da84fb5

12 files changed

Lines changed: 171 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
## 1.104.1-dev
1+
## 1.104.1
22

33
* Fix a bug where loud comments before `@use` rules could be emitted multiple
44
times under certain circumstances.
@@ -14,6 +14,11 @@
1414
This fixes a bug where `--watch` mode could enter an infinite loop recompiling
1515
the same CSS file over and over.
1616

17+
* Sass now sets the modification time of output files to the time compilation
18+
*started* rather than the time it *ended*. This ensures that, if a source file
19+
is modified during compilation, `--watch` and `--update` mode will recompile
20+
the outputs to include the new source file contents.
21+
1722
## 1.104.0
1823

1924
* **Potentially breaking compatibility fix:** Colors now convert the special

lib/src/executable/compile_stylesheet.dart

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ Future<void> _compileStylesheetWithoutErrorHandling(
8585
String? destination, {
8686
bool ifModified = false,
8787
}) async {
88+
var compilationStarted = DateTime.now();
8889
var importer = FilesystemImporter.cwd;
8990
if (ifModified) {
9091
try {
@@ -192,18 +193,25 @@ Future<void> _compileStylesheetWithoutErrorHandling(
192193
} else {
193194
ensureDir(p.dirname(destination));
194195
writeFile(destination, "${error.toCssString()}\n");
196+
_trySetModificationTime(destination, compilationStarted);
195197
}
196198
}
197199
rethrow;
198200
}
199201

200202
var css = result.css;
201-
css += _writeSourceMap(options, result.sourceMap, destination);
203+
css += _writeSourceMap(
204+
options,
205+
result.sourceMap,
206+
destination,
207+
compilationStarted,
208+
);
202209
if (destination == null) {
203210
if (css.isNotEmpty) print(css);
204211
} else {
205212
ensureDir(p.dirname(destination));
206213
writeFile(destination, "$css\n");
214+
_trySetModificationTime(destination, compilationStarted);
207215
}
208216

209217
if (options.quiet || (!options.update && !options.watch)) return;
@@ -238,6 +246,7 @@ String _writeSourceMap(
238246
ExecutableOptions options,
239247
SingleMapping? sourceMap,
240248
String? destination,
249+
DateTime compilationStarted,
241250
) {
242251
if (sourceMap == null) return "";
243252

@@ -268,6 +277,7 @@ String _writeSourceMap(
268277
var sourceMapPath = '${destination!}.map';
269278
ensureDir(p.dirname(sourceMapPath));
270279
writeFile(sourceMapPath, sourceMapText);
280+
_trySetModificationTime(sourceMapPath, compilationStarted);
271281

272282
url = p.toUri(p.relative(sourceMapPath, from: p.dirname(destination)));
273283
}
@@ -289,6 +299,20 @@ void _tryDelete(String path) {
289299
}
290300
}
291301

302+
/// Backdate [path] to [time], ignoring failures.
303+
///
304+
/// We use the time that a compilation started as the modification time for
305+
/// output files so that if a source file is modified during compilation, it
306+
/// will be considered newer than the output and compilation will run again
307+
/// (immediately for `--watch` or on the next invocation for `--update`).
308+
void _trySetModificationTime(String path, DateTime time) {
309+
try {
310+
setModificationTime(path, time);
311+
} on FileSystemException {
312+
// The output is still usable with its actual modification time.
313+
}
314+
}
315+
292316
/// Return a Record of `(exitCode, error, stackTrace)` for the given error.
293317
(int, String, String?) _getErrorWithStackTrace(
294318
int exitCode,

lib/src/io/interface.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ String realpath(String path) => throw '';
8181
/// Returns the modification time of the file at [path].
8282
DateTime modificationTime(String path) => throw '';
8383

84+
/// Sets the modification time of the file at [path] to [time].
85+
void setModificationTime(String path, DateTime time) => throw '';
86+
8487
/// Returns the value of the environment variable with the given [name], or
8588
/// `null` if it's not set.
8689
String? getEnvironmentVariable(String name) => throw '';

lib/src/io/js.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,18 @@ DateTime modificationTime(String path) {
248248
);
249249
}
250250

251+
void setModificationTime(String path, DateTime time) {
252+
if (!isNodeJs) {
253+
throw UnsupportedError(
254+
"setModificationTime() is only supported on Node.js",
255+
);
256+
}
257+
return _systemErrorToFileSystemException(() {
258+
var seconds = time.millisecondsSinceEpoch / 1000;
259+
fs.utimesSync(path, seconds, seconds);
260+
});
261+
}
262+
251263
String? getEnvironmentVariable(String name) {
252264
var env = _process?.env;
253265
return env == null ? null : getProperty(env as Object, name) as String?;

lib/src/io/vm.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ DateTime modificationTime(String path) {
101101
return stat.modified;
102102
}
103103

104+
void setModificationTime(String path, DateTime time) =>
105+
io.File(path).setLastModifiedSync(time);
106+
104107
String? getEnvironmentVariable(String name) => io.Platform.environment[name];
105108

106109
Future<Stream<WatchEvent>> watchDir(String path, {bool poll = false}) async {

pkg/sass-parser/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
## 0.4.55-dev
1+
## 0.4.55
22

33
* Omit an extra newline that was being added to the end of `Rule.selector` in
44
the indented syntax.

pkg/sass-parser/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "sass-parser",
3-
"version": "0.4.55-dev",
3+
"version": "0.4.55",
44
"description": "A PostCSS-compatible wrapper of the official Sass parser",
55
"repository": "sass/dart-sass",
66
"author": "Google Inc.",

pkg/sass_api/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
## 17.10.1-dev
1+
## 17.10.1
22

33
* Omit an extra newline that was being added to the end of `StyleRule.selector`
44
in the indented syntax.

pkg/sass_api/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: sass_api
22
# Note: Every time we add a new Sass AST node, we need to bump the *major*
33
# version because it's a breaking change for anyone who's implementing the
44
# visitor interface(s).
5-
version: 17.10.1-dev
5+
version: 17.10.1
66
description: Additional APIs for Dart Sass.
77
homepage: https://github.com/sass/dart-sass
88

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: sass
2-
version: 1.104.1-dev
2+
version: 1.104.1
33
description: A Sass implementation in Dart.
44
homepage: https://github.com/sass/dart-sass
55

0 commit comments

Comments
 (0)