diff --git a/.changes/fs-replace-notify-debouncer.md b/.changes/fs-replace-notify-debouncer.md
new file mode 100644
index 00000000..86b190eb
--- /dev/null
+++ b/.changes/fs-replace-notify-debouncer.md
@@ -0,0 +1,6 @@
+---
+"fs": "patch"
+"fs-js": "patch"
+---
+
+Replace `notify-debouncer-mini` with `notify-debouncer-full`. [(plugins-workspace#885)](https://github.com/tauri-apps/plugins-workspace/pull/885)
diff --git a/Cargo.lock b/Cargo.lock
index abf9e6ce..e8e6dcc5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1739,6 +1739,15 @@ dependencies = [
"rustc_version",
]
+[[package]]
+name = "file-id"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6584280525fb2059cba3db2c04abf947a1a29a45ddae89f3870f8281704fafc9"
+dependencies = [
+ "windows-sys 0.48.0",
+]
+
[[package]]
name = "filetime"
version = "0.2.22"
@@ -3574,15 +3583,17 @@ dependencies = [
]
[[package]]
-name = "notify-debouncer-mini"
-version = "0.4.1"
+name = "notify-debouncer-full"
+version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d40b221972a1fc5ef4d858a2f671fb34c75983eb385463dff3780eeff6a9d43"
+checksum = "49f5dab59c348b9b50cf7f261960a20e389feb2713636399cd9082cd4b536154"
dependencies = [
"crossbeam-channel",
+ "file-id",
"log",
"notify",
- "serde",
+ "parking_lot",
+ "walkdir",
]
[[package]]
@@ -5999,7 +6010,7 @@ dependencies = [
"anyhow",
"glob",
"notify",
- "notify-debouncer-mini",
+ "notify-debouncer-full",
"serde",
"serde_repr",
"tauri",
diff --git a/examples/api/src-tauri/Cargo.toml b/examples/api/src-tauri/Cargo.toml
index 7645f026..a70a8a71 100644
--- a/examples/api/src-tauri/Cargo.toml
+++ b/examples/api/src-tauri/Cargo.toml
@@ -20,7 +20,7 @@ serde = { workspace = true }
tiny_http = "0.11"
log = { workspace = true }
tauri-plugin-log = { path = "../../../plugins/log", version = "2.0.0-alpha.6" }
-tauri-plugin-fs = { path = "../../../plugins/fs", version = "2.0.0-alpha.7" }
+tauri-plugin-fs = { path = "../../../plugins/fs", version = "2.0.0-alpha.7", features = [ "watch" ] }
tauri-plugin-clipboard-manager = { path = "../../../plugins/clipboard-manager", version = "2.0.0-alpha.6" }
tauri-plugin-dialog = { path = "../../../plugins/dialog", version = "2.0.0-alpha.7" }
tauri-plugin-http = { path = "../../../plugins/http", features = [ "multipart" ], version = "2.0.0-alpha.9" }
diff --git a/examples/api/src/views/FileSystem.svelte b/examples/api/src/views/FileSystem.svelte
index 081d26ab..dce83663 100644
--- a/examples/api/src/views/FileSystem.svelte
+++ b/examples/api/src/views/FileSystem.svelte
@@ -9,6 +9,11 @@
let img;
let file;
let renameTo;
+ let watchPath = "";
+ let watchDebounceDelay = 0;
+ let watchRecursive = false;
+ let unwatchFn;
+ let unwatchPath = "";
function getDir() {
const dirSelect = document.getElementById("dir");
@@ -141,6 +146,41 @@
function setSrc() {
img.src = convertFileSrc(path);
}
+
+ function watch() {
+ unwatch();
+ if (watchPath) {
+ onMessage(`Watching ${watchPath} for changes`);
+ let options = {
+ recursive: watchRecursive,
+ delayMs: parseInt(watchDebounceDelay),
+ };
+ if (options.delayMs === 0) {
+ fs.watchImmediate(watchPath, onMessage, options)
+ .then((fn) => {
+ unwatchFn = fn;
+ unwatchPath = watchPath;
+ })
+ .catch(onMessage);
+ } else {
+ fs.watch(watchPath, onMessage, options)
+ .then((fn) => {
+ unwatchFn = fn;
+ unwatchPath = watchPath;
+ })
+ .catch(onMessage);
+ }
+ }
+ }
+
+ function unwatch() {
+ if (unwatchFn) {
+ onMessage(`Stopped watching ${unwatchPath} for changes`);
+ unwatchFn();
+ }
+ unwatchFn = undefined;
+ unwatchPath = undefined;
+ }
@@ -175,6 +215,33 @@
{/if}
+
+ Watch
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/fs/Cargo.toml b/plugins/fs/Cargo.toml
index a3273731..7ea7da8d 100644
--- a/plugins/fs/Cargo.toml
+++ b/plugins/fs/Cargo.toml
@@ -21,7 +21,7 @@ anyhow = "1"
uuid = { version = "1", features = [ "v4" ] }
glob = "0.3"
notify = { version = "6", optional = true, features = [ "serde" ] }
-notify-debouncer-mini = { version = "0.4", optional = true, features = [ "serde" ] }
+notify-debouncer-full = { version = "0.3", optional = true }
[features]
-watch = [ "notify", "notify-debouncer-mini" ]
+watch = [ "notify", "notify-debouncer-full" ]
diff --git a/plugins/fs/guest-js/index.ts b/plugins/fs/guest-js/index.ts
index 3d650e16..feb1d663 100644
--- a/plugins/fs/guest-js/index.ts
+++ b/plugins/fs/guest-js/index.ts
@@ -1098,8 +1098,8 @@ interface DebouncedWatchOptions extends WatchOptions {
/**
* @since 2.0.0
*/
-type RawEvent = {
- type: RawEventKind;
+type WatchEvent = {
+ type: WatchEventKind;
paths: string[];
attrs: unknown;
};
@@ -1107,28 +1107,60 @@ type RawEvent = {
/**
* @since 2.0.0
*/
-type RawEventKind =
- | "any "
- | {
- access?: unknown;
- }
- | {
- create?: unknown;
- }
- | {
- modify?: unknown;
- }
+type WatchEventKind =
+ | "any"
+ | { access: WatchEventKindAccess }
+ | { create: WatchEventKindCreate }
+ | { modify: WatchEventKindModify }
+ | { remove: WatchEventKindRemove }
+ | "other";
+
+/**
+ * @since 2.0.0
+ */
+type WatchEventKindAccess =
+ | { kind: "any" }
+ | { kind: "close"; mode: "any" | "execute" | "read" | "write" | "other" }
+ | { kind: "open"; mode: "any" | "execute" | "read" | "write" | "other" }
+ | { kind: "other" };
+
+/**
+ * @since 2.0.0
+ */
+type WatchEventKindCreate =
+ | { kind: "any" }
+ | { kind: "file" }
+ | { kind: "folder" }
+ | { kind: "other" };
+
+/**
+ * @since 2.0.0
+ */
+type WatchEventKindModify =
+ | { kind: "any" }
+ | { kind: "data"; mode: "any" | "size" | "content" | "other" }
| {
- remove?: unknown;
+ kind: "metadata";
+ mode:
+ | "any"
+ | "access-time"
+ | "write-time"
+ | "permissions"
+ | "ownership"
+ | "extended"
+ | "other";
}
- | "other";
+ | { kind: "name"; mode: "any" | "to" | "from" | "both" | "other" }
+ | { kind: "other" };
/**
* @since 2.0.0
*/
-type DebouncedEvent =
- | { kind: "Any"; path: string }[]
- | { kind: "AnyContinuous"; path: string }[];
+type WatchEventKindRemove =
+ | { kind: "any" }
+ | { kind: "file" }
+ | { kind: "folder" }
+ | { kind: "other" };
/**
* @since 2.0.0
@@ -1146,7 +1178,7 @@ async function unwatch(rid: number): Promise {
*/
async function watch(
paths: string | string[] | URL | URL[],
- cb: (event: DebouncedEvent) => void,
+ cb: (event: WatchEvent) => void,
options?: DebouncedWatchOptions,
): Promise {
const opts = {
@@ -1163,7 +1195,7 @@ async function watch(
}
}
- const onEvent = new Channel();
+ const onEvent = new Channel();
onEvent.onmessage = cb;
const rid: number = await invoke("plugin:fs|watch", {
@@ -1184,7 +1216,7 @@ async function watch(
*/
async function watchImmediate(
paths: string | string[] | URL | URL[],
- cb: (event: RawEvent) => void,
+ cb: (event: WatchEvent) => void,
options?: WatchOptions,
): Promise {
const opts = {
@@ -1201,7 +1233,7 @@ async function watchImmediate(
}
}
- const onEvent = new Channel();
+ const onEvent = new Channel();
onEvent.onmessage = cb;
const rid: number = await invoke("plugin:fs|watch", {
@@ -1232,8 +1264,12 @@ export type {
FileInfo,
WatchOptions,
DebouncedWatchOptions,
- DebouncedEvent,
- RawEvent,
+ WatchEvent,
+ WatchEventKind,
+ WatchEventKindAccess,
+ WatchEventKindCreate,
+ WatchEventKindModify,
+ WatchEventKindRemove,
UnwatchFn,
};
diff --git a/plugins/fs/src/watcher.rs b/plugins/fs/src/watcher.rs
index 05683586..07e46b61 100644
--- a/plugins/fs/src/watcher.rs
+++ b/plugins/fs/src/watcher.rs
@@ -3,7 +3,7 @@
// SPDX-License-Identifier: MIT
use notify::{Config, Event, RecommendedWatcher, RecursiveMode, Watcher};
-use notify_debouncer_mini::{new_debouncer, DebounceEventResult, Debouncer};
+use notify_debouncer_full::{new_debouncer, DebounceEventResult, Debouncer, FileIdMap};
use serde::Deserialize;
use tauri::{
ipc::Channel,
@@ -43,7 +43,7 @@ impl WatcherResource {
impl Resource for WatcherResource {}
enum WatcherKind {
- Debouncer(Debouncer),
+ Debouncer(Debouncer),
Watcher(RecommendedWatcher),
}
@@ -60,10 +60,10 @@ fn watch_raw(on_event: Channel, rx: Receiver>) {
fn watch_debounced(on_event: Channel, rx: Receiver) {
spawn(move || {
- while let Ok(event) = rx.recv() {
- if let Ok(event) = event {
+ while let Ok(Ok(events)) = rx.recv() {
+ for event in events {
// TODO: Should errors be emitted too?
- let _ = on_event.send(&event);
+ let _ = on_event.send(&event.event);
}
}
});
@@ -97,10 +97,9 @@ pub async fn watch(
let kind = if let Some(delay) = options.delay_ms {
let (tx, rx) = channel();
- let mut debouncer = new_debouncer(Duration::from_millis(delay), tx)?;
- let watcher = debouncer.watcher();
+ let mut debouncer = new_debouncer(Duration::from_millis(delay), None, tx)?;
for path in &resolved_paths {
- watcher.watch(path.as_ref(), mode)?;
+ debouncer.watcher().watch(path.as_ref(), mode)?;
}
watch_debounced(on_event, rx);
WatcherKind::Debouncer(debouncer)
@@ -130,14 +129,14 @@ pub async fn unwatch(app: AppHandle, rid: ResourceId) -> CommandR
for path in &watcher.paths {
debouncer.watcher().unwatch(path.as_ref()).map_err(|e| {
format!("failed to unwatch path: {} with error: {e}", path.display())
- })?
+ })?;
}
}
WatcherKind::Watcher(ref mut w) => {
for path in &watcher.paths {
w.unwatch(path.as_ref()).map_err(|e| {
format!("failed to unwatch path: {} with error: {e}", path.display())
- })?
+ })?;
}
}
}