375 lines
10 KiB
Go
375 lines
10 KiB
Go
package gojs
|
|
|
|
import (
|
|
"apigo.cloud/git/apigo/plugin"
|
|
"github.com/ssgo/u"
|
|
"path"
|
|
"reflect"
|
|
"regexp"
|
|
"runtime"
|
|
"strings"
|
|
)
|
|
|
|
type ArgInfo struct {
|
|
index int
|
|
isVariadic bool
|
|
isOutArg bool
|
|
isFunc bool
|
|
isSkip bool
|
|
isOptional bool
|
|
funcInArgs []ArgInfo
|
|
funcOutArgs []ArgInfo
|
|
Name string
|
|
Type string
|
|
}
|
|
|
|
var numberMatcher = regexp.MustCompile("[a-z0-9]*(int|float)[a-z0-9]*")
|
|
var mapMatcher = regexp.MustCompile("map\\[(\\w+)](\\w+)")
|
|
|
|
func makeArgTypeString(argType string) string {
|
|
if strings.Contains(argType, "interface {}") {
|
|
argType = strings.ReplaceAll(argType, "interface {}", "any")
|
|
}
|
|
if strings.HasPrefix(argType, "*") {
|
|
argType = argType[1:]
|
|
}
|
|
if strings.HasPrefix(argType, "[]") {
|
|
argType = argType[2:] + "[]"
|
|
}
|
|
if strings.Contains(argType, "int") || strings.Contains(argType, "float") {
|
|
argType = numberMatcher.ReplaceAllString(argType, "number")
|
|
}
|
|
if strings.Contains(argType, "bool") {
|
|
argType = strings.ReplaceAll(argType, "bool", "boolean")
|
|
}
|
|
if strings.Contains(argType, "booleanean") {
|
|
argType = strings.ReplaceAll(argType, "booleanean", "boolean")
|
|
}
|
|
if strings.HasPrefix(argType, "map[") {
|
|
argType = mapMatcher.ReplaceAllString(argType, "Map<$1, $2>")
|
|
}
|
|
//if strings.ContainsRune(argType, '.') {
|
|
// argType = argType[strings.LastIndexByte(argType, '.')+1:]
|
|
//}
|
|
return argType
|
|
}
|
|
|
|
func (argInfo *ArgInfo) String() string {
|
|
argType := argInfo.Type
|
|
if argInfo.isFunc {
|
|
argType = "(" + makeInArgsString(argInfo.funcInArgs) + ") => " + makeOutArgsString(argInfo.funcOutArgs)
|
|
} else {
|
|
argType = makeArgTypeString(argType)
|
|
}
|
|
if argInfo.isOutArg {
|
|
return argType
|
|
} else {
|
|
argName := argInfo.Name
|
|
if argName == "" {
|
|
argName = "arg" + u.String(argInfo.index+1)
|
|
}
|
|
if argInfo.isVariadic {
|
|
argName = "..." + argName
|
|
}
|
|
return argName + u.StringIf(argInfo.isOptional, "?: ", ": ") + argType
|
|
}
|
|
}
|
|
|
|
func makeInArgs(args []ArgInfo) []string {
|
|
arr := make([]string, 0)
|
|
for _, arg := range args {
|
|
if !arg.isSkip {
|
|
arr = append(arr, arg.String())
|
|
}
|
|
}
|
|
return arr
|
|
}
|
|
|
|
func makeInArgsString(args []ArgInfo) string {
|
|
return strings.Join(makeInArgs(args), ", ")
|
|
}
|
|
|
|
func makeOutArgsString(args []ArgInfo) string {
|
|
arr := makeInArgs(args)
|
|
if len(arr) == 0 {
|
|
return "void"
|
|
} else if len(arr) == 1 {
|
|
return arr[0]
|
|
} else {
|
|
return "[" + strings.Join(arr, ", ") + "]"
|
|
}
|
|
}
|
|
|
|
func makeFuncArgsNames(v reflect.Value, inArgs []ArgInfo, isMethod bool) {
|
|
fp := runtime.FuncForPC(v.Pointer())
|
|
file, lineNo := fp.FileLine(fp.Entry())
|
|
if file != "<autogenerated>" && u.FileExists(file) {
|
|
lines, _ := u.ReadFileLines(file)
|
|
if len(lines) >= lineNo {
|
|
line := lines[lineNo-1]
|
|
if !strings.Contains(line, "func") && lineNo-2 >= 0 && strings.Contains(lines[lineNo-2], "func") {
|
|
line = lines[lineNo-2]
|
|
}
|
|
line = strings.ReplaceAll(line, "interface{}", "any")
|
|
pos := strings.Index(line, "func")
|
|
if pos != -1 {
|
|
line = strings.TrimSpace(line[pos+4:])
|
|
if isMethod {
|
|
// skip method this arg
|
|
pos = strings.Index(line, "(")
|
|
if pos != -1 {
|
|
line = strings.TrimSpace(line[pos+1:])
|
|
makeFuncArgsName(line, inArgs[1:])
|
|
}
|
|
} else {
|
|
makeFuncArgsName(line, inArgs)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func makeFuncArgsName(line string, inArgs []ArgInfo) {
|
|
pos := strings.Index(line, "(")
|
|
if pos != -1 {
|
|
line = strings.TrimSpace(line[pos+1:])
|
|
for i := 0; i < len(inArgs); i++ {
|
|
// find param name
|
|
pos = strings.Index(line, " ")
|
|
// support combined args a, b string
|
|
pos1 := strings.Index(line, ",")
|
|
if pos1 != -1 && pos1 < pos {
|
|
inArgs[i].Name = line[0:pos1]
|
|
line = strings.TrimSpace(line[pos1+1:])
|
|
continue
|
|
}
|
|
if pos != -1 {
|
|
inArgs[i].Name = line[0:pos]
|
|
line = strings.TrimSpace(line[pos+1:])
|
|
}
|
|
// skip inline func
|
|
if strings.HasPrefix(line, "func") {
|
|
line = strings.TrimSpace(line[4:])
|
|
leftQuotes := 0
|
|
quoteStarted := false
|
|
for pos = 0; pos < len(line); pos++ {
|
|
if line[pos] == '(' {
|
|
leftQuotes++
|
|
quoteStarted = true
|
|
continue
|
|
}
|
|
if quoteStarted && line[pos] == ')' {
|
|
leftQuotes--
|
|
}
|
|
if quoteStarted && leftQuotes == 0 {
|
|
break
|
|
}
|
|
}
|
|
makeFuncArgsName(line, inArgs[i].funcInArgs)
|
|
line = strings.TrimSpace(line[pos+1:])
|
|
}
|
|
// skip ,
|
|
pos = strings.Index(line, ",")
|
|
if pos != -1 {
|
|
line = strings.TrimSpace(line[pos+1:])
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func makeFieldElemType(t reflect.Type, existsClasses *map[string]bool, classes *[]string, isSkip bool) string {
|
|
originT := t
|
|
if t.Kind() == reflect.Pointer {
|
|
t = t.Elem()
|
|
}
|
|
if t.Kind() == reflect.Map {
|
|
if t.Elem().Kind() == reflect.Struct && !isSkip {
|
|
makeClass(t.Elem(), existsClasses, classes)
|
|
}
|
|
et := t.Elem()
|
|
if et.Kind() == reflect.Pointer {
|
|
et = et.Elem()
|
|
}
|
|
if et.Kind() == reflect.Struct || et.Kind() == reflect.Map || (et.Kind() == reflect.Slice && et.Elem().Kind() != reflect.Uint8) {
|
|
return "Map<" + makeArgTypeString(t.Key().String()) + ", " + makeFieldElemType(t.Elem(), existsClasses, classes, isSkip) + ">"
|
|
}
|
|
return "Object"
|
|
} else if t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8 {
|
|
if t.Elem().Kind() == reflect.Struct && !isSkip {
|
|
makeClass(t.Elem(), existsClasses, classes)
|
|
}
|
|
return "Array<" + makeFieldElemType(t.Elem(), existsClasses, classes, isSkip) + ">"
|
|
} else if t.Kind() == reflect.Struct {
|
|
if !isSkip {
|
|
makeClass(originT, existsClasses, classes)
|
|
}
|
|
return makeArgTypeString(t.Name())
|
|
} else {
|
|
return makeArgTypeString(t.String())
|
|
}
|
|
}
|
|
|
|
func makeClass(t reflect.Type, existsClasses *map[string]bool, classes *[]string) {
|
|
originT := t
|
|
if t.Kind() == reflect.Pointer {
|
|
t = t.Elem()
|
|
}
|
|
if !(*existsClasses)[t.Name()] {
|
|
(*existsClasses)[t.Name()] = true
|
|
classItems := make([]string, 0)
|
|
implements := make([]string, 0)
|
|
for i := 0; i < t.NumField(); i++ {
|
|
if !t.Field(i).IsExported() {
|
|
continue
|
|
}
|
|
ft := t.Field(i).Type
|
|
if ft.Kind() == reflect.Pointer {
|
|
ft = ft.Elem()
|
|
}
|
|
ftStr := makeFieldElemType(ft, existsClasses, classes, false)
|
|
if t.Field(i).Anonymous {
|
|
implements = append(implements, ftStr)
|
|
continue
|
|
}
|
|
classItems = append(classItems, " "+u.GetLowerName(t.Field(i).Name)+": "+ftStr)
|
|
}
|
|
for i := 0; i < originT.NumMethod(); i++ {
|
|
if !originT.Method(i).IsExported() {
|
|
continue
|
|
}
|
|
inArgs, outArgs := makeFuncInOutArgs(originT.Method(i).Type, existsClasses, classes)
|
|
if len(inArgs) > 0 {
|
|
inArgs[0].isSkip = true
|
|
}
|
|
makeFuncArgsNames(originT.Method(i).Func, inArgs, true)
|
|
classItems = append(classItems, " "+u.GetLowerName(originT.Method(i).Name)+"("+makeInArgsString(inArgs)+"): "+makeOutArgsString(outArgs))
|
|
}
|
|
implementsStr := ""
|
|
if len(implements) > 0 {
|
|
implementsStr = " extends " + strings.Join(implements, ", ")
|
|
}
|
|
newClass := append([]string{}, "interface "+t.Name()+implementsStr+" {")
|
|
newClass = append(newClass, classItems...)
|
|
newClass = append(newClass, "}")
|
|
*classes = append(*classes, strings.Join(newClass, "\n"))
|
|
}
|
|
}
|
|
|
|
func makeFuncInOutArgs(t reflect.Type, existsClasses *map[string]bool, classes *[]string) (inArgs []ArgInfo, outArgs []ArgInfo) {
|
|
inArgs = make([]ArgInfo, t.NumIn())
|
|
outArgs = make([]ArgInfo, t.NumOut())
|
|
// *plugin.Context
|
|
for i := t.NumIn() - 1; i >= 0; i-- {
|
|
arg := t.In(i)
|
|
isSkip := false
|
|
if arg.String() == "*plugin.Context" {
|
|
isSkip = true
|
|
}
|
|
argInfo := ArgInfo{
|
|
index: i,
|
|
isOutArg: false,
|
|
isFunc: false,
|
|
isSkip: isSkip,
|
|
isOptional: arg.Kind() == reflect.Pointer,
|
|
isVariadic: t.IsVariadic() && i == t.NumIn()-1,
|
|
Name: "",
|
|
Type: makeFieldElemType(arg, existsClasses, classes, isSkip),
|
|
}
|
|
//originArg := arg
|
|
//if arg.Kind() == reflect.Pointer {
|
|
// arg = arg.Elem()
|
|
//}
|
|
if arg.Kind() == reflect.Func {
|
|
argInfo.isFunc = true
|
|
argInfo.funcInArgs, argInfo.funcOutArgs = makeFuncInOutArgs(arg, existsClasses, classes)
|
|
}
|
|
//if !argInfo.isSkip && arg.Kind() == reflect.Struct {
|
|
// makeClass(originArg, existsClasses, classes)
|
|
//}
|
|
inArgs[i] = argInfo
|
|
}
|
|
for i := t.NumOut() - 1; i >= 0; i-- {
|
|
arg := t.Out(i)
|
|
isSkip := false
|
|
if arg.String() == "error" {
|
|
isSkip = true
|
|
}
|
|
argInfo := ArgInfo{
|
|
index: i,
|
|
isOutArg: true,
|
|
isFunc: false,
|
|
isSkip: isSkip,
|
|
isVariadic: false,
|
|
Name: "",
|
|
Type: makeFieldElemType(arg, existsClasses, classes, isSkip),
|
|
}
|
|
//originArg := arg
|
|
//if arg.Kind() == reflect.Pointer {
|
|
// arg = arg.Elem()
|
|
//}
|
|
//if !argInfo.isSkip && arg.Kind() == reflect.Struct {
|
|
// makeClass(originArg, existsClasses, classes)
|
|
//}
|
|
outArgs[i] = argInfo
|
|
}
|
|
return inArgs, outArgs
|
|
}
|
|
|
|
func findObject(v reflect.Value, level int, existsClasses *map[string]bool) (codes []string, classes []string) {
|
|
codes = make([]string, 0)
|
|
classes = make([]string, 0)
|
|
if v.Kind() == reflect.Interface {
|
|
v = v.Elem()
|
|
}
|
|
originT := v.Type()
|
|
v = u.FinalValue(v)
|
|
t := originT
|
|
if t.Kind() == reflect.Pointer {
|
|
t = t.Elem()
|
|
}
|
|
indent := strings.Repeat(" ", level)
|
|
if t.Kind() == reflect.Map {
|
|
codes = append(codes, "{")
|
|
for _, k := range v.MapKeys() {
|
|
codes2, classes2 := findObject(v.MapIndex(k), level+1, existsClasses)
|
|
classes = append(classes, classes2...)
|
|
codes = append(codes, indent+" \""+k.String()+"\": "+strings.Join(codes2, "\n")+",")
|
|
}
|
|
codes = append(codes, indent+"}")
|
|
} else if t.Kind() == reflect.Struct {
|
|
makeClass(originT, existsClasses, &classes)
|
|
codes = append(codes, "null as "+t.Name())
|
|
} else if t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8 {
|
|
codes = append(codes, "[")
|
|
for i := 0; i < v.Len(); i++ {
|
|
codes2, classes2 := findObject(v.Index(i), level+1, existsClasses)
|
|
classes = append(classes, classes2...)
|
|
codes = append(codes, indent+" "+strings.Join(codes2, "\n")+",")
|
|
}
|
|
codes = append(codes, indent+"]")
|
|
} else if t.Kind() == reflect.Func {
|
|
inArgs, outArgs := makeFuncInOutArgs(t, existsClasses, &classes)
|
|
makeFuncArgsNames(v, inArgs, false)
|
|
codes = append(codes, "function ("+makeInArgsString(inArgs)+"): "+makeOutArgsString(outArgs)+" {return}")
|
|
} else {
|
|
codes = append(codes, u.Json(v.Interface()))
|
|
}
|
|
return codes, classes
|
|
}
|
|
|
|
func MakeAllPluginCode() {
|
|
for _, plg := range plugin.List() {
|
|
code := MakePluginCode(&plg)
|
|
_ = u.WriteFile(path.Join("plugins", plg.Id+".ts"), code)
|
|
}
|
|
}
|
|
|
|
func MakePluginCode(plg *plugin.Plugin) string {
|
|
if plg == nil {
|
|
return ""
|
|
}
|
|
existsClasses := make(map[string]bool)
|
|
codes, classes := findObject(reflect.ValueOf(plg.Objects), 0, &existsClasses)
|
|
return strings.Join(classes, "\n\n") + "\n\nexport default " + strings.Join(codes, "\n")
|
|
}
|