package main import ( "io" "log" "os" "os/exec" "sync" "syscall" "time" ) func runExpress(changes <-chan FileChange) { var currentProcess *exec.Cmd var mu sync.Mutex // Helper to start the express process startExpress := func() *exec.Cmd { cmd := exec.Command("../express/run.sh") cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Start(); err != nil { log.Printf("[express] Failed to start: %v", err) return nil } log.Printf("[express] Started (pid %d)", cmd.Process.Pid) // Monitor the process in background go func() { err := cmd.Wait() if err != nil { log.Printf("[express] Process exited: %v", err) } else { log.Printf("[express] Process exited normally") } }() return cmd } // Helper to stop the express process stopExpress := func(cmd *exec.Cmd) { if cmd == nil || cmd.Process == nil { return } log.Printf("[express] Stopping (pid %d)", cmd.Process.Pid) cmd.Process.Signal(syscall.SIGTERM) // Wait briefly for graceful shutdown done := make(chan struct{}) go func() { cmd.Wait() close(done) }() select { case <-done: log.Printf("[express] Stopped gracefully") case <-time.After(5 * time.Second): log.Printf("[express] Force killing") cmd.Process.Kill() } } // Helper to run the build runBuild := func() bool { log.Printf("[build] Starting ncc build...") cmd := exec.Command("sh", "-c", "cd ../express && ../cmd pnpm ncc build ./app.ts") stdout, _ := cmd.StdoutPipe() stderr, _ := cmd.StderrPipe() if err := cmd.Start(); err != nil { log.Printf("[build] Failed to start: %v", err) return false } // Copy output go io.Copy(os.Stdout, stdout) go io.Copy(os.Stderr, stderr) err := cmd.Wait() if err != nil { log.Printf("[build] Failed: %v", err) return false } log.Printf("[build] Success") return true } // Debounce timer var debounceTimer *time.Timer const debounceDelay = 100 * time.Millisecond for change := range changes { log.Printf("[watch] %s: %s", change.Operation, change.Path) // Reset debounce timer if debounceTimer != nil { debounceTimer.Stop() } debounceTimer = time.AfterFunc(debounceDelay, func() { if !runBuild() { log.Printf("[master] Build failed, keeping current process") return } mu.Lock() defer mu.Unlock() // Stop old process stopExpress(currentProcess) // Start new process currentProcess = startExpress() }) } }