-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
125 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { assertEquals } from "@std/assert"; | ||
import { test } from "@cross/test"; | ||
import { mktempdir, rm } from "./mod.ts"; | ||
import { join } from "@std/path"; | ||
import { type FileSystemEvent, FsWatcher } from "./watch.ts"; | ||
import { writeFile } from "../io/mod.ts"; | ||
|
||
test("FsWatcher watches for file changes", async () => { | ||
const watcher = FsWatcher(); | ||
const tempdir = await mktempdir(); | ||
const filePath = join(tempdir, "test.txt"); | ||
const events: FileSystemEvent[] = []; | ||
setTimeout(async () => { | ||
await writeFile(filePath, "Hello"); | ||
}, 1000); | ||
for await (const event of watcher.watch(tempdir)) { | ||
if (event.kind === "modify" && filePath == event.paths[0]) { | ||
events.push(event); | ||
break; // Stop watching after the creation event | ||
} | ||
} | ||
await new Promise((resolve) => setTimeout(resolve, 1000)); // Allow some time | ||
watcher.close(); | ||
await rm(tempdir, { recursive: true }); | ||
assertEquals(events.length, 1); | ||
assertEquals(events[0].kind, "modify"); | ||
assertEquals(events[0].paths[0], filePath); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import { CurrentRuntime, Runtime } from "@cross/runtime"; | ||
import { watch as nodeWatch } from "node:fs/promises"; | ||
import type { WatchOptions } from "node:fs"; | ||
import { join } from "@std/path"; | ||
|
||
export interface FileSystemWatcherOptions { | ||
recursive: boolean; | ||
signal?: AbortSignal; | ||
} | ||
export type FileSystemEventKind = | ||
| "error" | ||
| "any" | ||
| "access" | ||
| "create" | ||
| "modify" | ||
| "remove" | ||
| "other"; | ||
export interface FileSystemEvent { | ||
kind: FileSystemEventKind; | ||
paths: (string | undefined)[]; | ||
} | ||
export interface Watcher { | ||
watch( | ||
path: string, | ||
options?: FileSystemWatcherOptions, | ||
): AsyncIterable<FileSystemEvent>; | ||
close(): void; | ||
} | ||
export function FsWatcher(): Watcher { | ||
let denoWatcher: Deno.FsWatcher | undefined; | ||
let nodeWatcher: AsyncIterable<unknown>; | ||
const ac = new AbortController(); | ||
return { | ||
async *watch( | ||
path: string, | ||
options?: FileSystemWatcherOptions, | ||
): AsyncIterable<FileSystemEvent> { | ||
try { | ||
if (CurrentRuntime === Runtime.Deno) { | ||
denoWatcher = Deno.watchFs(path, options); | ||
for await (const event of denoWatcher) { | ||
yield event; | ||
} | ||
} else if ( | ||
CurrentRuntime === Runtime.Node || CurrentRuntime === Runtime.Bun | ||
) { | ||
const usedOptions: FileSystemWatcherOptions = options | ||
? options | ||
: { recursive: true }; | ||
if (!options?.signal) usedOptions.signal = ac.signal; | ||
nodeWatcher = await nodeWatch(path, usedOptions as WatchOptions); | ||
for await (const event of nodeWatcher) { | ||
//@ts-ignore cross-runtime | ||
if (event.filename) { | ||
const generatedEvent = { | ||
//@ts-ignore cross-runtime | ||
kind: (event.eventType === "change" | ||
? "modify" | ||
//@ts-ignore cross-runtime | ||
: event.eventType) as FileSystemEventKind, | ||
//@ts-ignore cross-runtime | ||
paths: [join(path, event.filename?.toString())], | ||
}; | ||
yield generatedEvent; | ||
} | ||
} | ||
} else { | ||
throw new Error("cross/watchFs: Runtime not supported."); | ||
} | ||
} catch (err) { | ||
if (err.name === "AbortError") { | ||
/* Ok! */ | ||
} else { | ||
throw new Error( | ||
"Cannot start asynchronous filesystem watcher using current runtime.", | ||
); | ||
} | ||
} | ||
}, | ||
close() { | ||
if (denoWatcher) { | ||
try { | ||
denoWatcher.close(); | ||
} catch (_e) { /* Ignore */ } | ||
} | ||
if (nodeWatcher) { | ||
ac?.abort(); | ||
} | ||
}, | ||
}; | ||
} |