fswatch
A filesystem watcher for Go. Pure Go, no cgo.
A Go port of the C++
@parcel/watcher, with substantial
modifications. See CHANGES.md for the list of differences and
bugfixes.
| GOOS | Watcher |
|---|---|
linux |
fanotify (default, kernel ≥ 5.13), inotify |
darwin |
FSEvents (default), kqueue |
windows |
ReadDirectoryChangesW |
freebsd, openbsd, netbsd, dragonfly |
kqueue |
Usage
package main
import (
"fmt"
"log"
"os"
"os/signal"
"github.com/microsoft/typescript-go/internal/fswatch"
)
func main() {
dir, _ := os.Getwd()
sub, err := fswatch.Default().WatchDirectory(dir, func(events []fswatch.Event, err error) {
if err != nil {
log.Println("watch error:", err)
return
}
for _, e := range events {
fmt.Printf("%s %s\n", e.Kind, e.Path)
}
})
if err != nil {
log.Fatal(err)
}
defer sub.Close()
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
<-c
}
Picking a watcher
Default() picks the best watcher for the current OS. To use a specific one:
sub, err := fswatch.Inotify().WatchDirectory(dir, callback, fswatch.WithRecursive())
All watchers exist on every platform. Use Available() to check support at
runtime, or just call WatchDirectory; it returns ErrUnavailable if the
watcher isn't supported.
Error handling
Errors are delivered through the callback. Use errors.Is to distinguish them:
ErrOverflow: some events were lost (kernel queue overflow). The watch is still active; rescan the directory to catch up.ErrWatchTerminated: the watch is dead (e.g. directory deleted). No further events will arrive. CallCloseto clean up.
if errors.Is(err, fswatch.ErrOverflow) {
rescanDir(dir)
return
}
if errors.Is(err, fswatch.ErrWatchTerminated) {
log.Println("watch terminated:", err)
sub.Close()
return
}
Behavior notes
-
Events arriving in quick succession are batched before delivery.
-
Event order within a batch is not guaranteed.
-
The callback runs on a library goroutine, not the caller's. Each watch's callback is serialized (never concurrent with itself).
-
Paths in events are absolute. Resolve symlinks before subscribing; backends report canonical paths:
realDir, err := filepath.EvalSymlinks(dir)