2024-02-17 12:55:08 +08:00
|
|
|
|
package gojs
|
|
|
|
|
|
|
|
|
|
import (
|
2024-10-02 00:07:02 +08:00
|
|
|
|
"apigo.cc/apigo/gojs/dop251/goja"
|
|
|
|
|
"apigo.cc/apigo/gojs/dop251/goja_nodejs/require"
|
|
|
|
|
"encoding/json"
|
2024-02-17 12:55:08 +08:00
|
|
|
|
"errors"
|
2024-02-18 13:20:58 +08:00
|
|
|
|
"fmt"
|
2024-02-17 12:55:08 +08:00
|
|
|
|
"github.com/ssgo/log"
|
2024-10-02 00:07:02 +08:00
|
|
|
|
"github.com/ssgo/tool/watcher"
|
2024-02-17 12:55:08 +08:00
|
|
|
|
"github.com/ssgo/u"
|
2024-03-24 12:03:07 +08:00
|
|
|
|
"os"
|
2024-10-02 00:07:02 +08:00
|
|
|
|
"os/signal"
|
2024-03-24 12:03:07 +08:00
|
|
|
|
"path/filepath"
|
2024-02-17 12:55:08 +08:00
|
|
|
|
"regexp"
|
|
|
|
|
"strings"
|
2024-10-02 00:07:02 +08:00
|
|
|
|
"sync"
|
|
|
|
|
"syscall"
|
|
|
|
|
"time"
|
2024-02-17 12:55:08 +08:00
|
|
|
|
)
|
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
type Map = map[string]any
|
|
|
|
|
|
|
|
|
|
type Module struct {
|
|
|
|
|
Object Map
|
|
|
|
|
ObjectMaker func(vm *goja.Runtime) Map
|
|
|
|
|
TsCode string
|
|
|
|
|
Desc string
|
|
|
|
|
Example string
|
2024-03-15 13:40:20 +08:00
|
|
|
|
}
|
2024-02-17 12:55:08 +08:00
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
var modules = map[string]Module{}
|
|
|
|
|
var modulesLock = sync.RWMutex{}
|
2024-03-24 12:03:07 +08:00
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
func Register(name string, mod Module) {
|
|
|
|
|
modulesLock.Lock()
|
|
|
|
|
modules[name] = mod
|
|
|
|
|
modulesLock.Unlock()
|
2024-03-24 12:03:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
func RunFile(file string, args ...any) (any, error) {
|
|
|
|
|
return Run(u.ReadFileN(file), file, args...)
|
2024-02-18 13:20:58 +08:00
|
|
|
|
}
|
2024-02-17 12:55:08 +08:00
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
func Run(code string, refFile string, args ...any) (any, error) {
|
|
|
|
|
rt := New()
|
|
|
|
|
var r any
|
|
|
|
|
err := rt.StartFromCode(code, refFile)
|
|
|
|
|
if err == nil {
|
|
|
|
|
r, err = rt.RunMain(args...)
|
2024-02-18 13:20:58 +08:00
|
|
|
|
}
|
2024-10-02 00:07:02 +08:00
|
|
|
|
return r, err
|
2024-02-18 13:20:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
var importModMatcher = regexp.MustCompile(`(?im)^\s*import\s+(.+?)\s+from\s+['"](.+?)['"]`)
|
|
|
|
|
var requireModMatcher = regexp.MustCompile(`(?im)^\s*(const|let|var)\s+(.+?)\s*=\s*require\s*\(\s*['"](.+?)['"]\s*\)`)
|
2024-03-16 22:50:05 +08:00
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
// var importLibMatcher = regexp.MustCompile(`(?im)^\s*(import)\s+(.+?)\s+from\s+['"][./\\\w:]+lib[/\\](.+?)(\.ts)?['"]`)
|
|
|
|
|
// var requireLibMatcher = regexp.MustCompile(`(?im)^\s*(const|let|var)\s+(.+?)\s*=\s*require\s*\(\s*['"][./\\\w:]+lib[/\\](.+?)(\.ts)?['"]\s*\)`)
|
|
|
|
|
var checkMainMatcher = regexp.MustCompile(`(?im)^\s*function\s+main\s*\(`)
|
2024-03-16 22:50:05 +08:00
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
type Runtime struct {
|
|
|
|
|
VM *goja.Runtime
|
|
|
|
|
required map[string]bool
|
|
|
|
|
file string
|
|
|
|
|
srcCode string
|
|
|
|
|
code string
|
|
|
|
|
moduleLoader func(string) string
|
|
|
|
|
started bool
|
2024-05-27 13:12:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
func (rt *Runtime) SetModuleLoader(fn func(filename string) string) {
|
|
|
|
|
rt.moduleLoader = fn
|
2024-05-27 13:12:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
func (rt *Runtime) GetCallStack() []string {
|
|
|
|
|
callStacks := make([]string, 0)
|
|
|
|
|
for _, stack := range rt.VM.CaptureCallStack(0, nil) {
|
|
|
|
|
callStacks = append(callStacks, stack.Position().String())
|
2024-05-27 13:12:15 +08:00
|
|
|
|
}
|
2024-10-02 00:07:02 +08:00
|
|
|
|
return callStacks
|
|
|
|
|
}
|
2024-05-27 13:12:15 +08:00
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
func (rt *Runtime) requireMod(name, realModName string) error {
|
|
|
|
|
if name != "" {
|
|
|
|
|
if rt.required[name] {
|
|
|
|
|
return nil
|
2024-05-27 13:12:15 +08:00
|
|
|
|
}
|
2024-10-02 00:07:02 +08:00
|
|
|
|
modulesLock.RLock()
|
|
|
|
|
mod, ok := modules[name]
|
|
|
|
|
modulesLock.RUnlock()
|
|
|
|
|
if !ok {
|
|
|
|
|
return errors.New("module not found: " + name)
|
2024-05-27 13:12:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
var err error
|
|
|
|
|
if mod.ObjectMaker != nil {
|
|
|
|
|
err = rt.VM.Set(realModName, mod.ObjectMaker(rt.VM))
|
2024-03-24 12:03:07 +08:00
|
|
|
|
} else {
|
2024-10-02 00:07:02 +08:00
|
|
|
|
err = rt.VM.Set(realModName, mod.Object)
|
2024-03-24 12:03:07 +08:00
|
|
|
|
}
|
2024-10-02 00:07:02 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
2024-05-27 13:12:15 +08:00
|
|
|
|
}
|
2024-10-02 00:07:02 +08:00
|
|
|
|
rt.required[name] = true
|
|
|
|
|
return nil
|
2024-03-24 12:03:07 +08:00
|
|
|
|
} else {
|
2024-10-02 00:07:02 +08:00
|
|
|
|
// 使用所有模块
|
|
|
|
|
var allModules = map[string]Module{}
|
|
|
|
|
modulesLock.RLock()
|
|
|
|
|
for k, v := range modules {
|
|
|
|
|
allModules[k] = v
|
|
|
|
|
}
|
|
|
|
|
modulesLock.RUnlock()
|
|
|
|
|
|
|
|
|
|
for k, v := range allModules {
|
|
|
|
|
if !rt.required[k] {
|
|
|
|
|
if err := rt.VM.Set(k, v); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
rt.required[k] = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
2024-05-27 13:12:15 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
func (rt *Runtime) makeImport(code string) (string, int, error) {
|
|
|
|
|
var modErr error
|
|
|
|
|
importCount := 0
|
|
|
|
|
code = requireModMatcher.ReplaceAllStringFunc(code, func(str string) string {
|
|
|
|
|
if m := requireModMatcher.FindStringSubmatch(str); m != nil && len(m) > 3 {
|
|
|
|
|
optName := m[1]
|
|
|
|
|
varName := m[2]
|
|
|
|
|
modName := m[3]
|
|
|
|
|
realModName := modName
|
|
|
|
|
if strings.ContainsRune(modName, '/') {
|
|
|
|
|
realModName = strings.ReplaceAll(realModName, "/", "_")
|
2024-03-15 13:40:20 +08:00
|
|
|
|
}
|
2024-10-02 00:07:02 +08:00
|
|
|
|
modulesLock.RLock()
|
|
|
|
|
_, ok := modules[modName]
|
|
|
|
|
modulesLock.RUnlock()
|
|
|
|
|
if !ok {
|
|
|
|
|
return str
|
2024-03-24 12:03:07 +08:00
|
|
|
|
}
|
2024-10-02 00:07:02 +08:00
|
|
|
|
importCount++
|
|
|
|
|
if modErr == nil {
|
|
|
|
|
if err := rt.requireMod(modName, realModName); err != nil {
|
|
|
|
|
modErr = err
|
2024-03-24 12:03:07 +08:00
|
|
|
|
}
|
2024-10-02 00:07:02 +08:00
|
|
|
|
}
|
|
|
|
|
if varName != realModName {
|
|
|
|
|
return fmt.Sprintf("%s %s = %s", optName, varName, realModName)
|
2024-03-24 12:03:07 +08:00
|
|
|
|
} else {
|
2024-10-02 00:07:02 +08:00
|
|
|
|
return ""
|
2024-03-15 13:40:20 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-10-02 00:07:02 +08:00
|
|
|
|
return str
|
2024-03-15 13:40:20 +08:00
|
|
|
|
})
|
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
if !rt.required["setTimeout"] && strings.Contains(code, "setTimeout") {
|
|
|
|
|
rt.required["setTimeout"] = true
|
|
|
|
|
err := rt.VM.Set("setTimeout", func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
|
|
|
|
args := MakeArgs(&argsIn, vm).Check(2)
|
|
|
|
|
callback := args.Func(0)
|
|
|
|
|
timeout := time.Duration(args.Int64(1)) * time.Millisecond
|
|
|
|
|
if callback != nil {
|
|
|
|
|
go func() {
|
|
|
|
|
if timeout > 0 {
|
|
|
|
|
time.Sleep(timeout)
|
|
|
|
|
}
|
|
|
|
|
if _, err := callback(args.This, args.Arguments[2:]...); err != nil {
|
|
|
|
|
//panic(vm.NewGoError(err))
|
|
|
|
|
}
|
|
|
|
|
}()
|
2024-05-27 13:12:15 +08:00
|
|
|
|
}
|
2024-10-02 00:07:02 +08:00
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
modErr = err
|
2024-05-27 13:12:15 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
return code, importCount, modErr
|
2024-05-27 13:12:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
func New() *Runtime {
|
|
|
|
|
vm := goja.New()
|
|
|
|
|
vm.GoData = map[string]any{
|
|
|
|
|
"logger": log.New(u.ShortUniqueId()),
|
|
|
|
|
}
|
2024-07-04 11:10:22 +08:00
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
rt := &Runtime{
|
|
|
|
|
VM: vm,
|
|
|
|
|
required: map[string]bool{},
|
|
|
|
|
}
|
2024-05-27 13:12:15 +08:00
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
// 处理模块引用
|
|
|
|
|
require.NewRegistryWithLoader(func(path string) ([]byte, error) {
|
|
|
|
|
modFile := path
|
|
|
|
|
if !filepath.IsAbs(modFile) {
|
|
|
|
|
modFile = filepath.Join(filepath.Dir(rt.file), modFile)
|
2024-05-27 13:12:15 +08:00
|
|
|
|
}
|
2024-10-02 00:07:02 +08:00
|
|
|
|
if !strings.HasSuffix(modFile, ".js") && !u.FileExists(modFile) {
|
|
|
|
|
modFile += ".js"
|
2024-02-17 12:55:08 +08:00
|
|
|
|
}
|
2024-10-02 00:07:02 +08:00
|
|
|
|
modCode := ""
|
|
|
|
|
if rt.moduleLoader != nil {
|
|
|
|
|
modCode = rt.moduleLoader(modFile)
|
|
|
|
|
}
|
|
|
|
|
if modCode == "" {
|
|
|
|
|
var err error
|
|
|
|
|
modCode, err = u.ReadFile(modFile)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
2024-05-27 16:52:19 +08:00
|
|
|
|
}
|
2024-05-27 13:12:15 +08:00
|
|
|
|
}
|
2024-10-02 00:07:02 +08:00
|
|
|
|
modCode = importModMatcher.ReplaceAllString(modCode, "let $1 = require('$2')")
|
|
|
|
|
modCode, _, _ = rt.makeImport(modCode)
|
|
|
|
|
return []byte(modCode), nil
|
|
|
|
|
}).Enable(rt.VM)
|
2024-02-17 12:55:08 +08:00
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
return rt
|
2024-02-18 13:20:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
func (rt *Runtime) StartFromFile(file string) error {
|
|
|
|
|
return rt.StartFromCode(u.ReadFileN(file), file)
|
2024-03-24 12:03:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
func (rt *Runtime) StartFromCode(code, refFile string) error {
|
|
|
|
|
if refFile != "" {
|
|
|
|
|
rt.file = refFile
|
2024-03-24 12:03:07 +08:00
|
|
|
|
}
|
2024-10-02 00:07:02 +08:00
|
|
|
|
if rt.file == "" {
|
|
|
|
|
rt.file = "main.js"
|
2024-03-15 13:40:20 +08:00
|
|
|
|
}
|
2024-03-24 12:03:07 +08:00
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
if absFile, err := filepath.Abs(rt.file); err == nil {
|
|
|
|
|
rt.file = absFile
|
|
|
|
|
}
|
|
|
|
|
refPath := filepath.Dir(refFile)
|
|
|
|
|
rt.VM.GoData["startPath"] = refPath
|
2024-07-04 11:10:22 +08:00
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
if rt.srcCode == "" {
|
|
|
|
|
rt.srcCode = code
|
|
|
|
|
}
|
|
|
|
|
rt.code = code
|
2024-07-04 11:10:22 +08:00
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
// 将 import 转换为 require
|
|
|
|
|
rt.code = importModMatcher.ReplaceAllString(rt.code, "let $1 = require('$2')")
|
2024-07-04 11:10:22 +08:00
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
// 按需加载引用
|
|
|
|
|
var importCount int
|
|
|
|
|
var modErr error
|
|
|
|
|
rt.code, importCount, modErr = rt.makeImport(rt.code)
|
2024-07-04 11:10:22 +08:00
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
// 如果没有import,默认import所有
|
|
|
|
|
if modErr == nil && importCount == 0 {
|
|
|
|
|
modErr = rt.requireMod("", "")
|
|
|
|
|
}
|
|
|
|
|
if modErr != nil {
|
|
|
|
|
return modErr
|
|
|
|
|
}
|
2024-03-15 13:40:20 +08:00
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
//fmt.Println(u.BCyan(rt.code))
|
2024-02-18 13:20:58 +08:00
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
// 初始化主函数
|
|
|
|
|
if !checkMainMatcher.MatchString(rt.code) {
|
|
|
|
|
rt.code = "function main(...args){" + rt.code + "}"
|
2024-03-24 12:03:07 +08:00
|
|
|
|
}
|
2024-10-02 00:07:02 +08:00
|
|
|
|
if _, err := rt.VM.RunScript(rt.file, rt.code); err != nil {
|
|
|
|
|
return err
|
2024-03-15 13:40:20 +08:00
|
|
|
|
} else {
|
2024-10-02 00:07:02 +08:00
|
|
|
|
rt.started = true
|
|
|
|
|
return nil
|
2024-03-24 12:03:07 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
func (rt *Runtime) RunMain(args ...any) (any, error) {
|
|
|
|
|
if !rt.started {
|
|
|
|
|
return nil, errors.New("runtime not started")
|
2024-03-24 12:03:07 +08:00
|
|
|
|
}
|
2024-10-02 00:07:02 +08:00
|
|
|
|
|
|
|
|
|
// 解析参数
|
|
|
|
|
for i, arg := range args {
|
|
|
|
|
if str, ok := arg.(string); ok {
|
|
|
|
|
var v interface{}
|
|
|
|
|
if err := json.Unmarshal([]byte(str), &v); err == nil {
|
|
|
|
|
args[i] = v
|
|
|
|
|
}
|
2024-03-24 12:03:07 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
if err := rt.VM.Set("__args", args); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
jsResult, err := rt.VM.RunScript("main", "main(...__args)")
|
2024-03-15 13:40:20 +08:00
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
var result any
|
|
|
|
|
if err == nil {
|
|
|
|
|
if jsResult != nil && !jsResult.Equals(goja.Undefined()) {
|
|
|
|
|
result = jsResult.Export()
|
2024-02-18 13:20:58 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-10-02 00:07:02 +08:00
|
|
|
|
return result, err
|
2024-02-18 13:20:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
func (rt *Runtime) SetGoData(name string, value any) {
|
|
|
|
|
rt.VM.GoData[name] = value
|
2024-05-27 13:12:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
func (rt *Runtime) GetGoData(name string) any {
|
|
|
|
|
return rt.VM.GoData[name]
|
|
|
|
|
}
|
2024-02-18 13:20:58 +08:00
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
func (rt *Runtime) Set(name string, value any) error {
|
|
|
|
|
return rt.VM.Set(name, value)
|
|
|
|
|
}
|
2024-02-18 13:20:58 +08:00
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
func (rt *Runtime) SetGlobal(global Map) error {
|
|
|
|
|
var err error
|
|
|
|
|
for k, v := range global {
|
|
|
|
|
if err = rt.VM.Set(k, v); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2024-03-24 12:03:07 +08:00
|
|
|
|
}
|
2024-10-02 00:07:02 +08:00
|
|
|
|
return nil
|
|
|
|
|
}
|
2024-02-18 13:20:58 +08:00
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
func (rt *Runtime) RunCode(code string) (any, error) {
|
|
|
|
|
//if !rt.started {
|
|
|
|
|
// return nil, errors.New("runtime not started")
|
|
|
|
|
//}
|
2024-02-18 13:20:58 +08:00
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
code = importModMatcher.ReplaceAllString(code, "let $1 = require('$2')")
|
|
|
|
|
code, _, _ = rt.makeImport(code)
|
2024-03-24 12:03:07 +08:00
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
jsResult, err := rt.VM.RunScript(rt.file, code)
|
|
|
|
|
var result any
|
|
|
|
|
if err == nil {
|
|
|
|
|
if jsResult != nil && !jsResult.Equals(goja.Undefined()) {
|
|
|
|
|
result = jsResult.Export()
|
2024-02-18 13:20:58 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-10-02 00:07:02 +08:00
|
|
|
|
return result, err
|
2024-02-18 13:20:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
//func RunFile(file string, args ...any) (any, error) {
|
|
|
|
|
// return Run(u.ReadFileN(file), file, args...)
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
type WatchRunner struct {
|
|
|
|
|
w *watcher.Watcher
|
2024-03-08 11:45:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
func (wr *WatchRunner) WaitForKill() {
|
|
|
|
|
exitCh := make(chan os.Signal, 1)
|
|
|
|
|
closeCh := make(chan bool, 1)
|
|
|
|
|
signal.Notify(exitCh, os.Interrupt, syscall.SIGTERM, syscall.SIGINT, syscall.SIGHUP)
|
|
|
|
|
go func() {
|
|
|
|
|
<-exitCh
|
|
|
|
|
closeCh <- true
|
|
|
|
|
}()
|
|
|
|
|
<-closeCh
|
|
|
|
|
wr.w.Stop()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (wr *WatchRunner) Stop() {
|
|
|
|
|
wr.w.Stop()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 {
|
|
|
|
|
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)))
|
2024-03-08 11:45:13 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
var isWaitingRun = false
|
|
|
|
|
onChange := func(filename string, event string) {
|
|
|
|
|
if !isWaitingRun {
|
|
|
|
|
_, _ = os.Stdout.WriteString("\x1b[3;J\x1b[H\x1b[2J")
|
|
|
|
|
isWaitingRun = true
|
|
|
|
|
go func() {
|
|
|
|
|
time.Sleep(time.Millisecond * 10)
|
|
|
|
|
isWaitingRun = false
|
|
|
|
|
run()
|
|
|
|
|
}()
|
2024-02-18 13:20:58 +08:00
|
|
|
|
}
|
2024-10-02 00:07:02 +08:00
|
|
|
|
fmt.Println(u.BYellow("[changed]"), filename)
|
|
|
|
|
}
|
|
|
|
|
_, _ = os.Stdout.WriteString("\x1b[3;J\x1b[H\x1b[2J")
|
|
|
|
|
watchStartPath := filepath.Dir(file)
|
|
|
|
|
fmt.Println(u.BMagenta("[watching root path]"), watchStartPath)
|
|
|
|
|
watchDirs := []string{watchStartPath}
|
|
|
|
|
watchTypes := []string{"js", "json", "yml"}
|
|
|
|
|
if extDirs != nil {
|
|
|
|
|
for _, v := range extDirs {
|
|
|
|
|
watchDirs = append(watchDirs, v)
|
2024-03-24 12:03:07 +08:00
|
|
|
|
}
|
2024-10-02 00:07:02 +08:00
|
|
|
|
}
|
|
|
|
|
if extTypes != nil {
|
|
|
|
|
for _, v := range extTypes {
|
|
|
|
|
watchTypes = append(watchTypes, v)
|
2024-03-24 12:03:07 +08:00
|
|
|
|
}
|
2024-10-02 00:07:02 +08:00
|
|
|
|
}
|
|
|
|
|
if w, err := watcher.Start(watchDirs, watchTypes, onChange); err == nil {
|
|
|
|
|
wr.w = w
|
|
|
|
|
go func() {
|
|
|
|
|
run()
|
|
|
|
|
}()
|
|
|
|
|
return wr, nil
|
|
|
|
|
} else {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2024-03-24 12:03:07 +08:00
|
|
|
|
}
|
2024-02-17 12:55:08 +08:00
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
func ExportForDev() string {
|
|
|
|
|
var allModules = map[string]Module{}
|
|
|
|
|
modulesLock.RLock()
|
|
|
|
|
for k, v := range modules {
|
|
|
|
|
allModules[k] = v
|
|
|
|
|
}
|
|
|
|
|
modulesLock.RUnlock()
|
|
|
|
|
|
|
|
|
|
dir, _ := os.Getwd()
|
|
|
|
|
nodeModulesPath := "node_modules"
|
|
|
|
|
packageJsonFile := "package.json"
|
|
|
|
|
for i := 0; i < 20; i++ {
|
|
|
|
|
nodePath := filepath.Join(dir, "node_modules")
|
|
|
|
|
if u.FileExists(nodePath) {
|
|
|
|
|
nodeModulesPath = nodePath
|
|
|
|
|
packageJsonFile = filepath.Join(dir, "package.json")
|
|
|
|
|
break
|
2024-05-27 13:12:15 +08:00
|
|
|
|
}
|
2024-10-02 00:07:02 +08:00
|
|
|
|
parentDir := filepath.Dir(dir)
|
|
|
|
|
if parentDir == dir {
|
|
|
|
|
break
|
2024-07-04 11:10:22 +08:00
|
|
|
|
}
|
2024-10-02 00:07:02 +08:00
|
|
|
|
dir = parentDir
|
|
|
|
|
}
|
2024-07-04 11:10:22 +08:00
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
oldPackageJson := u.ReadFileN(packageJsonFile)
|
|
|
|
|
insertModulesPostfix := ""
|
|
|
|
|
if oldPackageJson == "" {
|
|
|
|
|
oldPackageJson = "{\n \"devDependencies\": {\n\n }\n}"
|
|
|
|
|
} else if !strings.Contains(oldPackageJson, "\"devDependencies\": {") {
|
|
|
|
|
oldPackageJson = strings.Replace(oldPackageJson, "{", "{\n \"devDependencies\": {\n\n },", 1)
|
|
|
|
|
} else {
|
|
|
|
|
insertModulesPostfix = ","
|
|
|
|
|
}
|
2024-07-04 11:10:22 +08:00
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
insertModules := make([]string, 0)
|
|
|
|
|
imports := make([]string, len(allModules))
|
|
|
|
|
i := 0
|
|
|
|
|
for k, v := range allModules {
|
|
|
|
|
varName := k
|
|
|
|
|
tsPath := k
|
|
|
|
|
if strings.ContainsRune(k, '/') {
|
|
|
|
|
varName = varName[strings.LastIndex(varName, "/")+1:]
|
|
|
|
|
if os.PathSeparator != '/' {
|
|
|
|
|
tsPath = strings.ReplaceAll(tsPath, "/", string(os.PathSeparator))
|
|
|
|
|
}
|
2024-07-04 11:10:22 +08:00
|
|
|
|
}
|
2024-10-02 00:07:02 +08:00
|
|
|
|
_ = u.WriteFile(filepath.Join(nodeModulesPath, tsPath, "index.ts"), v.TsCode)
|
|
|
|
|
imports[i] = fmt.Sprintf("import %s from '%s'", varName, k)
|
|
|
|
|
i++
|
2024-07-04 11:10:22 +08:00
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
if !strings.Contains(oldPackageJson, "\""+k+"\":") {
|
|
|
|
|
insertModules = u.AppendUniqueString(insertModules, fmt.Sprint("\"", k, "\": \"v0.0.0\""))
|
2024-07-04 11:10:22 +08:00
|
|
|
|
}
|
2024-10-02 00:07:02 +08:00
|
|
|
|
}
|
2024-07-04 11:10:22 +08:00
|
|
|
|
|
2024-10-02 00:07:02 +08:00
|
|
|
|
if len(insertModules) > 0 {
|
|
|
|
|
newPackageJson := strings.Replace(oldPackageJson, "\"devDependencies\": {", "\"devDependencies\": {\n "+strings.Join(insertModules, ",\n ")+insertModulesPostfix, 1)
|
|
|
|
|
_ = u.WriteFile(packageJsonFile, newPackageJson)
|
2024-02-17 12:55:08 +08:00
|
|
|
|
}
|
2024-10-02 00:07:02 +08:00
|
|
|
|
return strings.Join(imports, "\n")
|
2024-02-17 12:55:08 +08:00
|
|
|
|
}
|