mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-04-21 11:26:15 +02:00
feat(fs/watch): migrate to notify-debouncer-full (#885)
* Add support for notify-debouncer-full * Add fs watch to demo * Remove notify-debouncer-mini * Rename RawEvent to WatchEvent * Add full type definition for EventKind * Remove `track file ids` option from fs watcher * Update plugins/fs/guest-js/index.ts
This commit is contained in:
@@ -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)
|
||||
Generated
+16
-5
@@ -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",
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col">
|
||||
@@ -175,6 +215,33 @@
|
||||
<button class="btn" on:click={stat}>Stat</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<h3>Watch</h3>
|
||||
|
||||
<input
|
||||
class="input grow"
|
||||
placeholder="Type the path to watch..."
|
||||
bind:value={watchPath}
|
||||
/>
|
||||
<br />
|
||||
<div>
|
||||
<label for="watch-debounce-delay">Debounce delay in milliseconds (`0` disables the debouncer)</label>
|
||||
<input
|
||||
class="input"
|
||||
id="watch-debounce-delay"
|
||||
bind:value={watchDebounceDelay}
|
||||
/>
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<input type="checkbox" id="watch-recursive" bind:checked={watchRecursive} />
|
||||
<label for="watch-recursive">Recursive</label>
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<button class="btn" on:click={watch}>Watch</button>
|
||||
<button class="btn" on:click={unwatch}>Unwatch</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
@@ -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" ]
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
| {
|
||||
remove?: unknown;
|
||||
}
|
||||
type WatchEventKind =
|
||||
| "any"
|
||||
| { access: WatchEventKindAccess }
|
||||
| { create: WatchEventKindCreate }
|
||||
| { modify: WatchEventKindModify }
|
||||
| { remove: WatchEventKindRemove }
|
||||
| "other";
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
*/
|
||||
type DebouncedEvent =
|
||||
| { kind: "Any"; path: string }[]
|
||||
| { kind: "AnyContinuous"; path: string }[];
|
||||
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" }
|
||||
| {
|
||||
kind: "metadata";
|
||||
mode:
|
||||
| "any"
|
||||
| "access-time"
|
||||
| "write-time"
|
||||
| "permissions"
|
||||
| "ownership"
|
||||
| "extended"
|
||||
| "other";
|
||||
}
|
||||
| { kind: "name"; mode: "any" | "to" | "from" | "both" | "other" }
|
||||
| { kind: "other" };
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
*/
|
||||
type WatchEventKindRemove =
|
||||
| { kind: "any" }
|
||||
| { kind: "file" }
|
||||
| { kind: "folder" }
|
||||
| { kind: "other" };
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
@@ -1146,7 +1178,7 @@ async function unwatch(rid: number): Promise<void> {
|
||||
*/
|
||||
async function watch(
|
||||
paths: string | string[] | URL | URL[],
|
||||
cb: (event: DebouncedEvent) => void,
|
||||
cb: (event: WatchEvent) => void,
|
||||
options?: DebouncedWatchOptions,
|
||||
): Promise<UnwatchFn> {
|
||||
const opts = {
|
||||
@@ -1163,7 +1195,7 @@ async function watch(
|
||||
}
|
||||
}
|
||||
|
||||
const onEvent = new Channel<DebouncedEvent>();
|
||||
const onEvent = new Channel<WatchEvent>();
|
||||
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<UnwatchFn> {
|
||||
const opts = {
|
||||
@@ -1201,7 +1233,7 @@ async function watchImmediate(
|
||||
}
|
||||
}
|
||||
|
||||
const onEvent = new Channel<RawEvent>();
|
||||
const onEvent = new Channel<WatchEvent>();
|
||||
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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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<RecommendedWatcher>),
|
||||
Debouncer(Debouncer<RecommendedWatcher, FileIdMap>),
|
||||
Watcher(RecommendedWatcher),
|
||||
}
|
||||
|
||||
@@ -60,10 +60,10 @@ fn watch_raw(on_event: Channel, rx: Receiver<notify::Result<Event>>) {
|
||||
|
||||
fn watch_debounced(on_event: Channel, rx: Receiver<DebounceEventResult>) {
|
||||
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<R: Runtime>(
|
||||
|
||||
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<R: Runtime>(app: AppHandle<R>, 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())
|
||||
})?
|
||||
})?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user