git commit -m "feat: initial release of Strata framework v0.1.0
- Static compiler with STRC pattern (Static Template Resolution with
Compartmentalized Layers)
- Template syntax: { } interpolation, { s-for }, { s-if/s-elif/s-else
}
- File types: .strata, .compiler.sts, .service.sts, .api.sts, .sts,
.scss
- CLI tools: strata dev, strata build, strata g (generators)
- create-strata scaffolding CLI with Pokemon API example
- Dev server with WebSocket HMR (Hot Module Replacement)
- Documentation: README, ARCHITECTURE, CHANGELOG, CONTRIBUTING,
LICENSE
This commit is contained in:
170
compiler/internal/watcher/watcher.go
Normal file
170
compiler/internal/watcher/watcher.go
Normal file
@@ -0,0 +1,170 @@
|
||||
package watcher
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
)
|
||||
|
||||
type FileType string
|
||||
|
||||
const (
|
||||
FileTypeStrata FileType = ".strata"
|
||||
FileTypeSTS FileType = ".sts"
|
||||
FileTypeSCSS FileType = ".scss"
|
||||
FileTypeTS FileType = ".ts"
|
||||
FileTypeConfig FileType = "strataconfig.ts"
|
||||
)
|
||||
|
||||
type ChangeEvent struct {
|
||||
Path string
|
||||
Type FileType
|
||||
Op fsnotify.Op
|
||||
IsConfig bool
|
||||
}
|
||||
|
||||
type Watcher struct {
|
||||
watcher *fsnotify.Watcher
|
||||
srcDir string
|
||||
onChange func(ChangeEvent)
|
||||
debounce time.Duration
|
||||
pending map[string]ChangeEvent
|
||||
mu sync.Mutex
|
||||
timer *time.Timer
|
||||
stopCh chan struct{}
|
||||
}
|
||||
|
||||
func New(srcDir string, onChange func(ChangeEvent)) (*Watcher, error) {
|
||||
fsWatcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
w := &Watcher{
|
||||
watcher: fsWatcher,
|
||||
srcDir: srcDir,
|
||||
onChange: onChange,
|
||||
debounce: 50 * time.Millisecond,
|
||||
pending: make(map[string]ChangeEvent),
|
||||
stopCh: make(chan struct{}),
|
||||
}
|
||||
|
||||
return w, nil
|
||||
}
|
||||
|
||||
func (w *Watcher) Start() error {
|
||||
// Add src directory recursively
|
||||
err := filepath.Walk(w.srcDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
return w.watcher.Add(path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Watch strataconfig.ts in root
|
||||
configPath := filepath.Join(filepath.Dir(w.srcDir), "strataconfig.ts")
|
||||
if _, err := os.Stat(configPath); err == nil {
|
||||
w.watcher.Add(filepath.Dir(configPath))
|
||||
}
|
||||
|
||||
go w.run()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *Watcher) run() {
|
||||
for {
|
||||
select {
|
||||
case event, ok := <-w.watcher.Events:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
w.handleEvent(event)
|
||||
|
||||
case err, ok := <-w.watcher.Errors:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
log.Printf("Watcher error: %v", err)
|
||||
|
||||
case <-w.stopCh:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Watcher) handleEvent(event fsnotify.Event) {
|
||||
// Skip non-relevant events
|
||||
if event.Op&(fsnotify.Write|fsnotify.Create|fsnotify.Remove) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Determine file type
|
||||
fileType := w.getFileType(event.Name)
|
||||
if fileType == "" {
|
||||
return
|
||||
}
|
||||
|
||||
isConfig := strings.HasSuffix(event.Name, "strataconfig.ts")
|
||||
|
||||
change := ChangeEvent{
|
||||
Path: event.Name,
|
||||
Type: fileType,
|
||||
Op: event.Op,
|
||||
IsConfig: isConfig,
|
||||
}
|
||||
|
||||
w.mu.Lock()
|
||||
w.pending[event.Name] = change
|
||||
|
||||
// Reset debounce timer
|
||||
if w.timer != nil {
|
||||
w.timer.Stop()
|
||||
}
|
||||
w.timer = time.AfterFunc(w.debounce, w.flush)
|
||||
w.mu.Unlock()
|
||||
}
|
||||
|
||||
func (w *Watcher) flush() {
|
||||
w.mu.Lock()
|
||||
pending := w.pending
|
||||
w.pending = make(map[string]ChangeEvent)
|
||||
w.mu.Unlock()
|
||||
|
||||
for _, event := range pending {
|
||||
w.onChange(event)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Watcher) getFileType(path string) FileType {
|
||||
ext := filepath.Ext(path)
|
||||
switch ext {
|
||||
case ".strata":
|
||||
return FileTypeStrata
|
||||
case ".sts":
|
||||
return FileTypeSTS
|
||||
case ".scss":
|
||||
return FileTypeSCSS
|
||||
case ".ts":
|
||||
if strings.HasSuffix(path, "strataconfig.ts") {
|
||||
return FileTypeConfig
|
||||
}
|
||||
return FileTypeTS
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (w *Watcher) Stop() {
|
||||
close(w.stopCh)
|
||||
w.watcher.Close()
|
||||
}
|
||||
Reference in New Issue
Block a user