gojs/gojs.go

684 lines
15 KiB
Go
Raw Normal View History

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-02-17 12:55:08 +08:00
2024-10-07 23:02:11 +08:00
type Program struct {
prg *goja.Program
startFile string
imports map[string]string
}
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) {
2024-10-07 23:02:11 +08:00
if code, err := u.ReadFile(file); err == nil {
return Run(code, file, args...)
} else {
return nil, err
}
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-07 23:02:11 +08:00
func RunProgram(plg *Program, args ...any) (any, error) {
rt := New()
var r any
err := rt.StartFromProgram(plg)
if err == nil {
r, err = rt.RunMain(args...)
}
return r, err
}
func CompileFile(file string) (*Program, error) {
if code, err := u.ReadFile(file); err == nil {
return CompileMain(code, file)
} else {
return nil, err
}
}
func CompileMain(code string, refFile string) (*Program, error) {
imports := makeImports(&code)
if !checkMainMatcher.MatchString(code) {
code = "function main(...Args){" + code + "}"
}
p, err := goja.Compile(refFile, code, false)
return &Program{prg: p, imports: imports, startFile: refFile}, err
}
func CompileCode(code string, refFile string) (*Program, error) {
imports := makeImports(&code)
p, err := goja.Compile(refFile, code, false)
return &Program{prg: p, imports: imports, startFile: refFile}, err
}
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-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-10-02 00:07:02 +08:00
type Runtime struct {
2024-10-07 23:02:11 +08:00
vm *goja.Runtime
2024-10-02 00:07:02 +08:00
required map[string]bool
file string
srcCode string
code string
moduleLoader func(string) string
started bool
2024-10-07 23:02:11 +08:00
dataLock sync.RWMutex
}
func (rt *Runtime) lock() {
rt.vm.Locker.Lock()
}
func (rt *Runtime) unlock() {
rt.vm.Locker.Unlock()
2024-05-27 13:12:15 +08:00
}
2024-10-07 23:02:11 +08:00
//func (rt *Runtime) Free() {
// rt.vm.GoData = nil
// rt.vm = nil
//}
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)
2024-10-07 23:02:11 +08:00
rt.lock()
stacks := rt.vm.CaptureCallStack(0, nil)
rt.unlock()
for _, stack := range stacks {
2024-10-02 00:07:02 +08:00
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-07 23:02:11 +08:00
func (rt *Runtime) requireSpecialMod(name string) error {
if rt.required[name] {
return nil
}
if name == "setTimeout" {
rt.required["setTimeout"] = true
fn := func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
args := MakeArgs(&argsIn, vm).Check(1)
callback := args.Func(0)
timeout := time.Duration(args.Int64(1)) * time.Millisecond
//locked := vm.Locker.TryLock()
//vm.Locker.Unlock()
//locked2 := vm.CallbackLocker.TryLock()
//vm.CallbackLocker.Unlock()
if callback != nil {
go func() {
if timeout > 0 {
time.Sleep(timeout)
}
//if !locked {
// vm.Locker.Lock()
//}
//if !locked2 {
// vm.CallbackLocker.Lock()
//}
if _, err := callback(args.This, args.Arguments[2:]...); err != nil {
//panic(vm.NewGoError(err))
}
}()
}
2024-10-02 00:07:02 +08:00
return nil
2024-05-27 13:12:15 +08:00
}
2024-10-07 23:02:11 +08:00
rt.lock()
err := rt.vm.Set("setTimeout", fn)
rt.unlock()
return err
}
2024-05-27 13:12:15 +08:00
2024-10-07 23:02:11 +08:00
return errors.New("module not found: " + name)
}
func (rt *Runtime) requireMod(modName, realModName string) error {
if rt.required[modName] {
2024-10-02 00:07:02 +08:00
return nil
2024-10-07 23:02:11 +08:00
}
modulesLock.RLock()
mod, ok := modules[modName]
modulesLock.RUnlock()
if !ok {
// 不在注册的模块中,尝试引入特殊模块
return rt.requireSpecialMod(modName)
}
var err error
rt.lock()
if mod.ObjectMaker != nil {
err = rt.vm.Set(realModName, mod.ObjectMaker(rt.vm))
2024-03-24 12:03:07 +08:00
} else {
2024-10-07 23:02:11 +08:00
err = rt.vm.Set(realModName, mod.Object)
}
rt.unlock()
if err != nil {
return err
}
rt.required[modName] = true
return nil
}
2024-10-02 00:07:02 +08:00
2024-10-07 23:02:11 +08:00
func (rt *Runtime) requireAllMod() error {
var allModules = map[string]Module{}
modulesLock.RLock()
for k, v := range modules {
allModules[k] = v
}
modulesLock.RUnlock()
for modName, mod := range allModules {
if !rt.required[modName] {
var err error
realModName := modName
if strings.ContainsRune(modName, '/') {
realModName = strings.ReplaceAll(realModName, "/", "_")
}
rt.lock()
if mod.ObjectMaker != nil {
err = rt.vm.Set(realModName, mod.ObjectMaker(rt.vm))
} else {
err = rt.vm.Set(realModName, mod.Object)
2024-10-02 00:07:02 +08:00
}
2024-10-07 23:02:11 +08:00
rt.unlock()
if err != nil {
return err
}
rt.required[modName] = true
2024-10-02 00:07:02 +08:00
}
2024-05-27 13:12:15 +08:00
}
2024-10-07 23:02:11 +08:00
if err := rt.requireSpecialMod("setTimeout"); err != nil {
return err
}
return nil
2024-05-27 13:12:15 +08:00
}
2024-10-07 23:02:11 +08:00
func makeImports(code *string) map[string]string {
importModules := map[string]string{}
*code = importModMatcher.ReplaceAllString(*code, "let $1 = require('$2')")
*code = requireModMatcher.ReplaceAllStringFunc(*code, func(str string) string {
2024-10-02 00:07:02 +08:00
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-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-07 23:02:11 +08:00
importModules[modName] = realModName
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-10-02 00:07:02 +08:00
return str
})
2024-10-07 23:02:11 +08:00
if strings.Contains(*code, "setTimeout") {
importModules["setTimeout"] = "setTimeout"
}
return importModules
}
func (rt *Runtime) setImports(importModules map[string]string) error {
var modErr error
for k, v := range importModules {
if modErr == nil {
if err := rt.requireMod(k, v); err != nil {
modErr = err
2024-05-27 13:12:15 +08:00
}
}
}
2024-10-07 23:02:11 +08:00
return modErr
2024-05-27 13:12:15 +08:00
}
2024-10-02 00:07:02 +08:00
func New() *Runtime {
vm := goja.New()
2024-10-02 00:07:02 +08:00
rt := &Runtime{
2024-10-07 23:02:11 +08:00
vm: vm,
2024-10-02 00:07:02 +08:00
required: map[string]bool{},
}
2024-05-27 13:12:15 +08:00
2024-10-07 23:02:11 +08:00
vm.GoData = map[string]any{
"logger": log.New(u.ShortUniqueId()),
}
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-07 23:02:11 +08:00
imports := makeImports(&modCode)
if err := rt.setImports(imports); err != nil {
return nil, err
}
2024-10-02 00:07:02 +08:00
return []byte(modCode), nil
2024-10-07 23:02:11 +08:00
}).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 {
2024-10-07 23:02:11 +08:00
if code, err := u.ReadFile(file); err == nil {
return rt.StartFromCode(code, file)
} else {
return err
}
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-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)
2024-10-07 23:02:11 +08:00
rt.SetGoData("startFile", refFile)
rt.SetGoData("startPath", refPath)
2024-10-02 00:07:02 +08:00
if rt.srcCode == "" {
rt.srcCode = code
}
rt.code = code
2024-10-02 00:07:02 +08:00
// 按需加载引用
2024-10-07 23:02:11 +08:00
//var importCount int
2024-10-02 00:07:02 +08:00
var modErr error
2024-10-07 23:02:11 +08:00
//rt.code, importCount, modErr = rt.makeImport(rt.code)
imports := makeImports(&rt.code)
if len(imports) == 0 {
modErr = rt.requireAllMod()
} else {
modErr = rt.setImports(imports)
2024-10-02 00:07:02 +08:00
}
2024-10-07 23:02:11 +08:00
//// 如果没有import默认import所有
//if modErr == nil && importCount == 0 {
// modErr = rt.requireMod("", "")
//}
2024-10-02 00:07:02 +08:00
if modErr != nil {
return modErr
}
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) {
2024-10-07 23:02:11 +08:00
rt.code = "function main(...Args){" + rt.code + "}"
2024-03-24 12:03:07 +08:00
}
2024-10-07 23:02:11 +08:00
rt.lock()
_, err := rt.vm.RunScript(rt.file, rt.code)
rt.unlock()
if err != nil {
return err
} else {
rt.started = true
return nil
}
}
func (rt *Runtime) StartFromProgram(plg *Program) error {
var modErr error
if len(plg.imports) == 0 {
modErr = rt.requireAllMod()
} else {
modErr = rt.setImports(plg.imports)
}
if modErr != nil {
return modErr
}
rt.SetGoData("startFile", plg.startFile)
rt.SetGoData("startPath", filepath.Dir(plg.startFile))
rt.lock()
_, err := rt.vm.RunProgram(plg.prg)
rt.unlock()
if err != nil {
2024-10-02 00:07:02 +08:00
return err
} 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-07 23:02:11 +08:00
rt.lock()
err := rt.vm.Set("__args", args)
rt.unlock()
if err != nil {
2024-10-02 00:07:02 +08:00
return nil, err
}
2024-10-07 23:02:11 +08:00
rt.lock()
r, err := rt.vm.RunScript("main", "main(...__args)")
rt.unlock()
return makeResult(r, err)
}
2024-10-07 23:02:11 +08:00
func (rt *Runtime) RunVM(callback func(vm *goja.Runtime) (any, error)) (any, error) {
rt.lock()
r, err := callback(rt.vm)
rt.unlock()
return r, 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) {
2024-10-07 23:02:11 +08:00
rt.dataLock.Lock()
rt.vm.GoData[name] = value
rt.dataLock.Unlock()
2024-05-27 13:12:15 +08:00
}
2024-10-02 00:07:02 +08:00
func (rt *Runtime) GetGoData(name string) any {
2024-10-07 23:02:11 +08:00
rt.dataLock.RLock()
defer rt.dataLock.RUnlock()
return rt.vm.GoData[name]
2024-10-02 00:07:02 +08:00
}
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 {
2024-10-07 23:02:11 +08:00
rt.lock()
defer rt.unlock()
return rt.vm.Set(name, value)
2024-10-02 00:07:02 +08:00
}
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
2024-10-07 23:02:11 +08:00
rt.lock()
defer rt.unlock()
2024-10-02 00:07:02 +08:00
for k, v := range global {
2024-10-07 23:02:11 +08:00
if err = rt.vm.Set(k, v); err != nil {
2024-10-02 00:07:02 +08:00
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-07 23:02:11 +08:00
func (rt *Runtime) RunFile(file string) (any, error) {
if code, err := u.ReadFile(file); err == nil {
imports := makeImports(&code)
if err := rt.setImports(imports); err != nil {
return nil, err
}
rt.lock()
r, err := rt.vm.RunScript(file, code)
rt.unlock()
return makeResult(r, err)
} else {
return nil, err
}
}
2024-10-02 00:07:02 +08:00
func (rt *Runtime) RunCode(code string) (any, error) {
2024-10-07 23:02:11 +08:00
imports := makeImports(&code)
if err := rt.setImports(imports); err != nil {
return nil, err
}
rt.lock()
r, err := rt.vm.RunScript("anonymous", code)
rt.unlock()
return makeResult(r, err)
}
2024-02-18 13:20:58 +08:00
2024-10-07 23:02:11 +08:00
func (rt *Runtime) RunProgram(prg *Program) (any, error) {
var r goja.Value
err := rt.setImports(prg.imports)
if err == nil {
rt.lock()
r, err = rt.vm.RunProgram(prg.prg)
rt.unlock()
}
return makeResult(r, err)
}
2024-03-24 12:03:07 +08:00
2024-10-07 23:02:11 +08:00
func makeResult(r goja.Value, err error) (any, error) {
2024-10-02 00:07:02 +08:00
var result any
if err == nil {
2024-10-07 23:02:11 +08:00
if r != nil && !r.Equals(goja.Undefined()) {
result = r.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
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-10-02 00:07:02 +08:00
dir = parentDir
}
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-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-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-10-02 00:07:02 +08:00
if !strings.Contains(oldPackageJson, "\""+k+"\":") {
insertModules = u.AppendUniqueString(insertModules, fmt.Sprint("\"", k, "\": \"v0.0.0\""))
}
2024-10-02 00:07:02 +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
}