Fix watching when path has a trailing slash. (#2294)
diff --git a/pkgs/watcher/CHANGELOG.md b/pkgs/watcher/CHANGELOG.md index ab621ca..0d64856 100644 --- a/pkgs/watcher/CHANGELOG.md +++ b/pkgs/watcher/CHANGELOG.md
@@ -1,3 +1,12 @@ +## 1.2.1-wip + +- Bug fix: versions before 1.2.0 would allow and ignore a trailing path + separator passed to `DirectoryWatcher` or `FileWatcher` constructors, restore + that behavior. +- In paths passed to `DirectoryWatcher` or `FileWatcher` constructors, remove + multiple adjacent separators and `.` and `..`, so they will not be returned in + events. + ## 1.2.0 - Polling watchers now check file sizes as well as "last modified" times, so
diff --git a/pkgs/watcher/lib/src/directory_watcher/linux/linux_directory_watcher.dart b/pkgs/watcher/lib/src/directory_watcher/linux/linux_directory_watcher.dart index eb93cc2..c24b390 100644 --- a/pkgs/watcher/lib/src/directory_watcher/linux/linux_directory_watcher.dart +++ b/pkgs/watcher/lib/src/directory_watcher/linux/linux_directory_watcher.dart
@@ -17,7 +17,7 @@ String get directory => path; LinuxDirectoryWatcher(String directory) - : super(directory, () => _LinuxDirectoryWatcher(directory)); + : super(directory, _LinuxDirectoryWatcher.new); } /// Linux directory watcher that watches using [WatchTreeRoot].
diff --git a/pkgs/watcher/lib/src/directory_watcher/polling/polling_directory_watcher.dart b/pkgs/watcher/lib/src/directory_watcher/polling/polling_directory_watcher.dart index 835a0b5..4beb556 100644 --- a/pkgs/watcher/lib/src/directory_watcher/polling/polling_directory_watcher.dart +++ b/pkgs/watcher/lib/src/directory_watcher/polling/polling_directory_watcher.dart
@@ -28,9 +28,9 @@ /// shorter will give more immediate feedback at the expense of doing more IO /// and higher CPU usage. Defaults to one second. PollingDirectoryWatcher(String directory, {Duration? pollingDelay}) - : super(directory, () { + : super(directory, (path) { return _PollingDirectoryWatcher( - directory, pollingDelay ?? const Duration(seconds: 1)); + path, pollingDelay ?? const Duration(seconds: 1)); }); }
diff --git a/pkgs/watcher/lib/src/directory_watcher/recursive/recursive_directory_watcher.dart b/pkgs/watcher/lib/src/directory_watcher/recursive/recursive_directory_watcher.dart index 3971f34..6c4bf5b 100644 --- a/pkgs/watcher/lib/src/directory_watcher/recursive/recursive_directory_watcher.dart +++ b/pkgs/watcher/lib/src/directory_watcher/recursive/recursive_directory_watcher.dart
@@ -25,9 +25,9 @@ RecursiveDirectoryWatcher(String directory, {required bool runInIsolate}) : super( directory, - () => runInIsolate - ? IsolateRecursiveDirectoryWatcher(directory) - : ManuallyClosedRecursiveDirectoryWatcher(directory)); + (path) => runInIsolate + ? IsolateRecursiveDirectoryWatcher(path) + : ManuallyClosedRecursiveDirectoryWatcher(path)); } /// Manually closed directory watcher that watches using [WatchedDirectoryTree].
diff --git a/pkgs/watcher/lib/src/file_watcher/native.dart b/pkgs/watcher/lib/src/file_watcher/native.dart index 75efb76..9862092 100644 --- a/pkgs/watcher/lib/src/file_watcher/native.dart +++ b/pkgs/watcher/lib/src/file_watcher/native.dart
@@ -16,7 +16,7 @@ /// Single-file notifications are much simpler than those for multiple files, so /// this doesn't need to be split out into multiple OS-specific classes. class NativeFileWatcher extends ResubscribableWatcher implements FileWatcher { - NativeFileWatcher(String path) : super(path, () => _NativeFileWatcher(path)); + NativeFileWatcher(String path) : super(path, _NativeFileWatcher.new); } class _NativeFileWatcher implements FileWatcher, ManuallyClosedWatcher {
diff --git a/pkgs/watcher/lib/src/file_watcher/polling.dart b/pkgs/watcher/lib/src/file_watcher/polling.dart index e87649d..3b4abc2 100644 --- a/pkgs/watcher/lib/src/file_watcher/polling.dart +++ b/pkgs/watcher/lib/src/file_watcher/polling.dart
@@ -13,7 +13,7 @@ /// Periodically polls a file for changes. class PollingFileWatcher extends ResubscribableWatcher implements FileWatcher { PollingFileWatcher(String path, {Duration? pollingDelay}) - : super(path, () { + : super(path, (path) { return _PollingFileWatcher( path, pollingDelay ?? const Duration(seconds: 1)); });
diff --git a/pkgs/watcher/lib/src/resubscribable.dart b/pkgs/watcher/lib/src/resubscribable.dart index a2c37f8..b67cd32 100644 --- a/pkgs/watcher/lib/src/resubscribable.dart +++ b/pkgs/watcher/lib/src/resubscribable.dart
@@ -4,6 +4,8 @@ import 'dart:async'; +import 'package:path/path.dart' as p; + import '../watcher.dart'; /// A wrapper for [ManuallyClosedWatcher] that encapsulates support for closing @@ -21,7 +23,7 @@ /// takes a factory function that produces instances of the inner class. abstract class ResubscribableWatcher implements Watcher { /// The factory function that produces instances of the inner class. - final ManuallyClosedWatcher Function() _factory; + final ManuallyClosedWatcher Function(String path) _factory; @override final String path; @@ -39,14 +41,15 @@ /// Creates a new [ResubscribableWatcher] wrapping the watchers /// emitted by [_factory]. - ResubscribableWatcher(this.path, this._factory) { + ResubscribableWatcher(String path, this._factory) + : path = _normalizeAndStripTrailingSeparator(path) { late ManuallyClosedWatcher watcher; late StreamSubscription<WatchEvent> subscription; _eventsController = StreamController<WatchEvent>.broadcast( onListen: () async { final completer = _readyCompleter; - watcher = _factory(); + watcher = _factory(this.path); subscription = watcher.events.listen(_eventsController.add, onError: _eventsController.addError, onDone: _eventsController.close); @@ -78,3 +81,13 @@ /// resources. void close(); } + +/// Normalizes [path] then strips any extra trailing separator. +/// +/// Removes adjacent separators, `.` and `..` but does not convert to absolute. +String _normalizeAndStripTrailingSeparator(String path) { + path = p.normalize(path); + return path.length > 1 && path.endsWith(p.separator) + ? path.substring(0, path.length - 1) + : path; +}
diff --git a/pkgs/watcher/pubspec.yaml b/pkgs/watcher/pubspec.yaml index 0f2be5b..5faf63b 100644 --- a/pkgs/watcher/pubspec.yaml +++ b/pkgs/watcher/pubspec.yaml
@@ -1,5 +1,5 @@ name: watcher -version: 1.2.0 +version: 1.2.1-wip description: >- A file system watcher. It monitors changes to contents of directories and sends notifications when files have been added, removed, or modified.
diff --git a/pkgs/watcher/test/directory_watcher/file_tests.dart b/pkgs/watcher/test/directory_watcher/file_tests.dart index da9a1e9..4f4b26d 100644 --- a/pkgs/watcher/test/directory_watcher/file_tests.dart +++ b/pkgs/watcher/test/directory_watcher/file_tests.dart
@@ -112,6 +112,25 @@ expect(await queue3HasNext, false); }); + // Regression test for https://github.com/dart-lang/tools/issues/2293. + test('works with trailing path separator', () async { + await startWatcher(exactPath: '${d.sandbox}${Platform.pathSeparator}'); + + writeFile('a.txt'); + await expectAddEvent('a.txt'); + }); + + test('normalizes many adjacent separators and ..', () async { + createDir('a'); + final separator = Platform.pathSeparator; + await startWatcher( + exactPath: + '${d.sandbox}${separator * 5}a${separator * 4}b${separator * 3}..'); + + writeFile('a/a.txt'); + await expectAddEvent('a/a.txt'); + }); + test('does not notify for files that already exist when started', () async { // Make some pre-existing files. writeFile('a.txt');