From 38b618ce351b0e6432b5912e97e16df259dc36b5 Mon Sep 17 00:00:00 2001 From: ctrl+d <52456860+DanieleAurilio@users.noreply.github.com> Date: Mon, 25 Nov 2024 16:38:52 +0100 Subject: [PATCH] fix(runtime/ops): Fix watchfs remove event (#27041) Fix related to #26906. Currently, if a file is removed, no event is emitted because the file path no longer exists. As a result, [this check](https://github.com/denoland/deno/blob/12b377247be2b74155ded3a678ff2996ef3d7c9f/runtime/ops/fs_events.rs#L149) returns false. With this PR, an additional check is introduced to verify if the file exists. If the file does not exist, a custom "remove" event is emitted. This change is necessary because, based on tests conducted on macOS and Linux (Ubuntu 24.04.1 LTS), Linux emits a "rename" event instead of a "remove" event when a file is deleted. Introducing a dedicated "remove" event ensures consistent and clearer behavior across platforms. --- runtime/ops/fs_events.rs | 15 +++++++++++++++ tests/unit/fs_events_test.ts | 30 ++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/runtime/ops/fs_events.rs b/runtime/ops/fs_events.rs index a91722f91a..f6e5ceff5c 100644 --- a/runtime/ops/fs_events.rs +++ b/runtime/ops/fs_events.rs @@ -109,6 +109,14 @@ fn starts_with_canonicalized(path: &Path, prefix: &str) -> bool { } } +fn is_file_removed(event_path: &PathBuf) -> bool { + let exists_path = std::fs::exists(event_path); + match exists_path { + Ok(res) => !res, + Err(_) => false, + } +} + #[derive(Debug, thiserror::Error)] pub enum FsEventsError { #[error(transparent)] @@ -150,6 +158,13 @@ fn start_watcher( }) }) { let _ = sender.try_send(Ok(event.clone())); + } else if event.paths.iter().any(is_file_removed) { + let remove_event = FsEvent { + kind: "remove", + paths: event.paths.clone(), + flag: None, + }; + let _ = sender.try_send(Ok(remove_event)); } } } diff --git a/tests/unit/fs_events_test.ts b/tests/unit/fs_events_test.ts index cc2f2cd571..7489626b9f 100644 --- a/tests/unit/fs_events_test.ts +++ b/tests/unit/fs_events_test.ts @@ -45,6 +45,14 @@ async function makeTempDir(): Promise { return testDir; } +async function makeTempFile(): Promise { + const testFile = await Deno.makeTempFile(); + // The watcher sometimes witnesses the creation of it's own root + // directory. Delay a bit. + await delay(100); + return testFile; +} + Deno.test( { permissions: { read: true, write: true } }, async function watchFsBasic() { @@ -155,3 +163,25 @@ Deno.test( assert(done); }, ); + +Deno.test( + { permissions: { read: true, write: true } }, + async function watchFsRemove() { + const testFile = await makeTempFile(); + using watcher = Deno.watchFs(testFile); + async function waitForRemove() { + for await (const event of watcher) { + if (event.kind === "remove") { + return event; + } + } + } + const eventPromise = waitForRemove(); + + await Deno.remove(testFile); + + // Expect zero events. + const event = await eventPromise; + assertEquals(event!.kind, "remove"); + }, +);