@@ -35,6 +35,9 @@ class _NativeFileWatcher implements FileWatcher, ManuallyClosedWatcher {
3535
3636 StreamSubscription <List <FileSystemEvent >>? _subscription;
3737
38+ /// On MacOS only, whether the file existed on startup.
39+ bool ? _existedAtStartup;
40+
3841 _NativeFileWatcher (this .path) {
3942 _listen ();
4043
@@ -43,21 +46,42 @@ class _NativeFileWatcher implements FileWatcher, ManuallyClosedWatcher {
4346 _readyCompleter.complete ();
4447 }
4548
46- void _listen () {
49+ void _listen () async {
50+ var file = File (path);
51+
4752 // Batch the events together so that we can dedup them.
48- _subscription = File (path)
49- .watch ()
50- .batchEvents ()
51- .listen (_onBatch, onError: _eventsController.addError, onDone: _onDone);
53+ var stream = file.watch ().batchEvents ();
54+
55+ if (Platform .isMacOS) {
56+ var existedAtStartupFuture = file.exists ();
57+ // Delay processing watch events until the existence check finishes.
58+ stream = stream.asyncMap ((event) async {
59+ _existedAtStartup ?? = await existedAtStartupFuture;
60+ return event;
61+ });
62+ }
63+
64+ _subscription = stream.listen (_onBatch,
65+ onError: _eventsController.addError, onDone: _onDone);
5266 }
5367
54- void _onBatch (List <FileSystemEvent > batch) {
68+ void _onBatch (List <FileSystemEvent > batch) async {
5569 if (batch.any ((event) => event.type == FileSystemEvent .delete)) {
5670 // If the file is deleted, the underlying stream will close. We handle
5771 // emitting our own REMOVE event in [_onDone].
5872 return ;
5973 }
6074
75+ if (Platform .isMacOS) {
76+ // On MacOS, a spurious `create` event can be received for a file that is
77+ // created just before the `watch`. If the file existed at startup then it
78+ // should be ignored.
79+ if (_existedAtStartup! &&
80+ batch.every ((event) => event.type == FileSystemEvent .create)) {
81+ return ;
82+ }
83+ }
84+
6185 _eventsController.add (WatchEvent (ChangeType .MODIFY , path));
6286 }
6387
0 commit comments