add __startExec、__runFile

add Watch for custom watch action
fix bug of WatchRun
This commit is contained in:
Star 2024-10-26 22:11:41 +08:00
parent 2832100201
commit e9c25edc20

209
gojs.go
View File

@ -98,20 +98,25 @@ func WaitAll() {
} }
} }
modulesLock.RUnlock() modulesLock.RUnlock()
stopped := false
sigCh := make(chan os.Signal, 1) sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM, syscall.SIGINT, syscall.SIGHUP) signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM, syscall.SIGINT, syscall.SIGHUP)
go func() { go func() {
for { for {
select { select {
case sig := <-sigCh: case sig := <-sigCh:
if stopped {
break
}
// send signal // send signal
for _, fn := range onSignals { for _, fn := range onSignals {
fn(sig) fn(sig)
} }
// kill // kill
if sig == syscall.SIGINT || sig == syscall.SIGTERM { if sig == syscall.SIGINT || sig == syscall.SIGTERM {
WatcherOnKill() if wr != nil {
wr.Stop()
}
for _, fn := range onKills { for _, fn := range onKills {
fn() fn()
} }
@ -120,11 +125,13 @@ func WaitAll() {
} }
} }
}() }()
// 一些必须在主线程运行的代码在这运行(例如 apigo.cc/gojs/client // 一些必须在主线程运行的代码在这运行(例如 apigo.cc/gojs/client
for _, fn := range runInMainFuncs { for _, fn := range runInMainFuncs {
fn() fn()
} }
if wr != nil {
wr.Wait()
}
if len(waits) > 0 { if len(waits) > 0 {
waitChan := make(chan bool, len(waits)) waitChan := make(chan bool, len(waits))
for _, fn := range waits { for _, fn := range waits {
@ -134,6 +141,8 @@ func WaitAll() {
}(fn) }(fn)
} }
<-waitChan <-waitChan
stopped = true
sigCh <- syscall.SIGTERM
} }
} }
@ -155,10 +164,10 @@ func Run(code string, refFile string, args ...any) (any, error) {
return r, err return r, err
} }
func RunProgram(plg *Program, args ...any) (any, error) { func RunProgram(prg *Program, args ...any) (any, error) {
rt := New() rt := New()
var r any var r any
err := rt.StartFromProgram(plg) err := rt.StartFromProgram(prg)
if err == nil { if err == nil {
r, err = rt.RunMain(args...) r, err = rt.RunMain(args...)
} }
@ -345,6 +354,7 @@ func New() *Runtime {
vm: vm, vm: vm,
required: map[string]bool{}, required: map[string]bool{},
} }
vm.Set("__startExec", os.Args[0])
vm.GoData = map[string]any{ vm.GoData = map[string]any{
"logger": log.New(u.ShortUniqueId()), "logger": log.New(u.ShortUniqueId()),
@ -402,6 +412,7 @@ func (rt *Runtime) StartFromCode(code, refFile string) error {
refPath := filepath.Dir(refFile) refPath := filepath.Dir(refFile)
rt.SetGoData("startFile", refFile) rt.SetGoData("startFile", refFile)
rt.SetGoData("startPath", refPath) rt.SetGoData("startPath", refPath)
rt.vm.Set("__startFile", refFile)
if rt.srcCode == "" { if rt.srcCode == "" {
rt.srcCode = code rt.srcCode = code
@ -434,6 +445,7 @@ func (rt *Runtime) StartFromCode(code, refFile string) error {
rt.code = "function main(...Args){" + rt.code + "}" rt.code = "function main(...Args){" + rt.code + "}"
} }
rt.lock() rt.lock()
rt.vm.Set("__runFile", rt.file)
_, err := rt.vm.RunScript(rt.file, rt.code) _, err := rt.vm.RunScript(rt.file, rt.code)
rt.unlock() rt.unlock()
if err != nil { if err != nil {
@ -444,20 +456,22 @@ func (rt *Runtime) StartFromCode(code, refFile string) error {
} }
} }
func (rt *Runtime) StartFromProgram(plg *Program) error { func (rt *Runtime) StartFromProgram(prg *Program) error {
var modErr error var modErr error
if len(plg.imports) == 0 { if len(prg.imports) == 0 {
modErr = rt.requireAllMod() modErr = rt.requireAllMod()
} else { } else {
modErr = rt.setImports(plg.imports) modErr = rt.setImports(prg.imports)
} }
if modErr != nil { if modErr != nil {
return modErr return modErr
} }
rt.SetGoData("startFile", plg.startFile) rt.SetGoData("startFile", prg.startFile)
rt.SetGoData("startPath", filepath.Dir(plg.startFile)) rt.SetGoData("startPath", filepath.Dir(prg.startFile))
rt.lock() rt.lock()
_, err := rt.vm.RunProgram(plg.prg) rt.vm.Set("__startFile", prg.startFile)
rt.vm.Set("__runFile", prg.startFile)
_, err := rt.vm.RunProgram(prg.prg)
rt.unlock() rt.unlock()
if err != nil { if err != nil {
return err return err
@ -538,6 +552,7 @@ func (rt *Runtime) RunFile(file string) (any, error) {
return nil, err return nil, err
} }
rt.lock() rt.lock()
rt.vm.Set("__runFile", file)
r, err := rt.vm.RunScript(file, code) r, err := rt.vm.RunScript(file, code)
rt.unlock() rt.unlock()
return makeResult(r, err) return makeResult(r, err)
@ -562,6 +577,7 @@ func (rt *Runtime) RunProgram(prg *Program) (any, error) {
err := rt.setImports(prg.imports) err := rt.setImports(prg.imports)
if err == nil { if err == nil {
rt.lock() rt.lock()
rt.vm.Set("__runFile", prg.startFile)
r, err = rt.vm.RunProgram(prg.prg) r, err = rt.vm.RunProgram(prg.prg)
rt.unlock() rt.unlock()
} }
@ -580,12 +596,7 @@ func makeResult(r goja.Value, err error) (any, error) {
type WatchRunner struct { type WatchRunner struct {
w *watcher.Watcher w *watcher.Watcher
} stopCh chan bool
func WatcherOnKill() {
if wr != nil {
wr.Stop()
}
} }
// func (wr *WatchRunner) WaitForKill() { // func (wr *WatchRunner) WaitForKill() {
@ -600,18 +611,17 @@ func WatcherOnKill() {
// wr.w.Stop() // wr.w.Stop()
// } // }
func (wr *WatchRunner) Wait() {
<-wr.stopCh
}
func (wr *WatchRunner) Stop() { func (wr *WatchRunner) Stop() {
wr.w.Stop() wr.w.Stop()
wr.stopCh <- true
wr = nil wr = nil
} }
var wr *WatchRunner func (wr *WatchRunner) SetModuleLoader(rt *Runtime) {
func WatchRun(file string, extDirs, extTypes []string, args ...any) (*WatchRunner, error) {
wr = &WatchRunner{}
run := func() {
rt := New()
if wr.w != nil {
rt.SetModuleLoader(func(filename string) string { rt.SetModuleLoader(func(filename string) string {
filePath := filepath.Dir(filename) filePath := filepath.Dir(filename)
needWatch := true needWatch := true
@ -628,6 +638,52 @@ func WatchRun(file string, extDirs, extTypes []string, args ...any) (*WatchRunne
return u.ReadFileN(filename) return u.ReadFileN(filename)
}) })
} }
var wr *WatchRunner
func Watch(files, types []string, onRun func([]string)) (*WatchRunner, error) {
wr = &WatchRunner{}
var isWaitingRun = false
changes := make([]string, 0)
changesLock := sync.Mutex{}
onChange := func(filename string, event string) {
changesLock.Lock()
changes = append(changes, filename)
changesLock.Unlock()
if !isWaitingRun {
_, _ = os.Stdout.WriteString("\x1b[3;J\x1b[H\x1b[2J")
isWaitingRun = true
go func() {
time.Sleep(time.Millisecond * 10)
isWaitingRun = false
changesLock.Lock()
changes2 := make([]string, len(changes))
copy(changes2, changes)
changes = make([]string, 0)
changesLock.Unlock()
onRun(changes2)
}()
}
fmt.Println(u.BYellow("[changed]"), filename)
}
_, _ = os.Stdout.WriteString("\x1b[3;J\x1b[H\x1b[2J")
if w, err := watcher.Start(files, types, onChange); err == nil {
wr.w = w
go func() {
onRun(changes)
}()
wr.stopCh = make(chan bool, 1)
return wr, nil
} else {
wr = nil
return nil, err
}
}
func WatchRun(file string, args ...any) (*WatchRunner, error) {
return Watch([]string{file}, []string{"js", "json", "yml"}, func(files []string) {
rt := New()
wr.SetModuleLoader(rt)
err := rt.StartFromFile(file) err := rt.StartFromFile(file)
result, err := rt.RunMain(args...) result, err := rt.RunMain(args...)
if err != nil { if err != nil {
@ -636,45 +692,74 @@ func WatchRun(file string, extDirs, extTypes []string, args ...any) (*WatchRunne
} else if result != nil { } else if result != nil {
fmt.Println(u.Cyan(u.JsonP(result))) fmt.Println(u.Cyan(u.JsonP(result)))
} }
} })
// wr = &WatchRunner{}
// run := func() {
// rt := New()
// if wr.w != nil {
// rt.SetModuleLoader(func(filename string) string {
// filePath := filepath.Dir(filename)
// needWatch := true
// for _, v := range wr.w.WatchList() {
// if v == filePath {
// needWatch = false
// break
// }
// }
// if needWatch {
// fmt.Println(u.BMagenta("[watching module path]"), filePath)
// _ = wr.w.Add(filePath)
// }
// return u.ReadFileN(filename)
// })
// }
// err := rt.StartFromFile(file)
// result, err := rt.RunMain(args...)
// if err != nil {
// fmt.Println(u.BRed(err.Error()))
// fmt.Println(u.Red(" " + strings.Join(rt.GetCallStack(), "\n ")))
// } else if result != nil {
// fmt.Println(u.Cyan(u.JsonP(result)))
// }
// }
var isWaitingRun = false // var isWaitingRun = false
onChange := func(filename string, event string) { // onChange := func(filename string, event string) {
if !isWaitingRun { // if !isWaitingRun {
_, _ = os.Stdout.WriteString("\x1b[3;J\x1b[H\x1b[2J") // _, _ = os.Stdout.WriteString("\x1b[3;J\x1b[H\x1b[2J")
isWaitingRun = true // isWaitingRun = true
go func() { // go func() {
time.Sleep(time.Millisecond * 10) // time.Sleep(time.Millisecond * 10)
isWaitingRun = false // isWaitingRun = false
run() // run()
}() // }()
} // }
fmt.Println(u.BYellow("[changed]"), filename) // fmt.Println(u.BYellow("[changed]"), filename)
} // }
_, _ = os.Stdout.WriteString("\x1b[3;J\x1b[H\x1b[2J") // _, _ = os.Stdout.WriteString("\x1b[3;J\x1b[H\x1b[2J")
watchStartPath := filepath.Dir(file) // watchStartPath := filepath.Dir(file)
fmt.Println(u.BMagenta("[watching root path]"), watchStartPath) // fmt.Println(u.BMagenta("[watching root path]"), watchStartPath)
watchDirs := []string{watchStartPath} // watchDirs := []string{watchStartPath}
watchTypes := []string{"js", "json", "yml"} // watchTypes := []string{"js", "json", "yml"}
if extDirs != nil { // if extDirs != nil {
for _, v := range extDirs { // for _, v := range extDirs {
watchDirs = append(watchDirs, v) // watchDirs = append(watchDirs, v)
} // }
} // }
if extTypes != nil { // if extTypes != nil {
for _, v := range extTypes { // for _, v := range extTypes {
watchTypes = append(watchTypes, v) // watchTypes = append(watchTypes, v)
} // }
} // }
if w, err := watcher.Start(watchDirs, watchTypes, onChange); err == nil { // if w, err := watcher.Start(watchDirs, watchTypes, onChange); err == nil {
wr.w = w // wr.w = w
go func() { // go func() {
run() // run()
}() // }()
return wr, nil // return wr, nil
} else { // } else {
return nil, err // return nil, err
} // }
} }
func ExportForDev() string { func ExportForDev() string {