74 lines
1.7 KiB
Go
74 lines
1.7 KiB
Go
// 热重载驱动:fsnotify 文件监听 + 防抖(热更新.md §4.5)。
|
||
package mods
|
||
|
||
import (
|
||
"fmt"
|
||
"path/filepath"
|
||
"sync"
|
||
"time"
|
||
|
||
"github.com/fsnotify/fsnotify"
|
||
)
|
||
|
||
// Watcher 脚本文件监听器(开发模式)。
|
||
type Watcher struct {
|
||
w *fsnotify.Watcher
|
||
mu sync.Mutex
|
||
reload map[string]func() error // 脚本路径 → 重载函数
|
||
timers map[string]*time.Timer // 防抖计时器(500ms)
|
||
}
|
||
|
||
// NewWatcher 创建监听器。
|
||
func NewWatcher() (*Watcher, error) {
|
||
w, err := fsnotify.NewWatcher()
|
||
if err != nil {
|
||
return nil, fmt.Errorf("mods.NewWatcher: %w", err)
|
||
}
|
||
return &Watcher{w: w, reload: make(map[string]func() error), timers: make(map[string]*time.Timer)}, nil
|
||
}
|
||
|
||
// WatchFile 监听一个脚本文件:变化后防抖触发 reload(热更新.md §4.5 防抖 500ms)。
|
||
func (w *Watcher) WatchFile(path string, reload func() error) error {
|
||
if err := w.w.Add(filepath.Dir(path)); err != nil {
|
||
return fmt.Errorf("mods.WatchFile %s: %w", path, err)
|
||
}
|
||
w.mu.Lock()
|
||
w.reload[path] = reload
|
||
w.mu.Unlock()
|
||
return nil
|
||
}
|
||
|
||
// Run 事件循环(goroutine):文件变化 → 防抖 → 重载。
|
||
func (w *Watcher) Run() {
|
||
for {
|
||
select {
|
||
case ev, ok := <-w.w.Events:
|
||
if !ok {
|
||
return
|
||
}
|
||
if ev.Op&(fsnotify.Write|fsnotify.Create) == 0 {
|
||
continue
|
||
}
|
||
w.mu.Lock()
|
||
fn, ok := w.reload[ev.Name]
|
||
if !ok {
|
||
w.mu.Unlock()
|
||
continue
|
||
}
|
||
if t, exists := w.timers[ev.Name]; exists {
|
||
t.Stop()
|
||
}
|
||
reloadFn := fn
|
||
w.timers[ev.Name] = time.AfterFunc(500*time.Millisecond, func() { _ = reloadFn() })
|
||
w.mu.Unlock()
|
||
case _, ok := <-w.w.Errors:
|
||
if !ok {
|
||
return
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Close 停止监听。
|
||
func (w *Watcher) Close() error { return w.w.Close() }
|