131 lines
3.0 KiB
Go
131 lines
3.0 KiB
Go
|
|
package js
|
||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"reflect"
|
||
|
|
"sort"
|
||
|
|
"strings"
|
||
|
|
|
||
|
|
"apigo.cc/go/jsmod"
|
||
|
|
)
|
||
|
|
|
||
|
|
// Doc generates a TypeScript definition (.d.ts) for all registered Go modules.
|
||
|
|
// This is designed to be fed into an AI (LLM) to provide context for low-code development.
|
||
|
|
func Doc() string {
|
||
|
|
var sb strings.Builder
|
||
|
|
sb.WriteString("// TypeScript Definitions for Go/JS Low-Code Environment\n\n")
|
||
|
|
|
||
|
|
modules := jsmod.GetModules()
|
||
|
|
keys := make([]string, 0, len(modules))
|
||
|
|
for k := range modules {
|
||
|
|
keys = append(keys, k)
|
||
|
|
}
|
||
|
|
sort.Strings(keys)
|
||
|
|
|
||
|
|
sb.WriteString("declare namespace go {\n")
|
||
|
|
for _, modName := range keys {
|
||
|
|
exports := modules[modName]
|
||
|
|
sb.WriteString(fmt.Sprintf(" namespace %s {\n", modName))
|
||
|
|
|
||
|
|
expKeys := make([]string, 0, len(exports))
|
||
|
|
for k := range exports {
|
||
|
|
expKeys = append(expKeys, k)
|
||
|
|
}
|
||
|
|
sort.Strings(expKeys)
|
||
|
|
|
||
|
|
for _, name := range expKeys {
|
||
|
|
val := exports[name]
|
||
|
|
sb.WriteString(fmt.Sprintf(" %s\n", formatExport(name, val)))
|
||
|
|
}
|
||
|
|
sb.WriteString(" }\n")
|
||
|
|
}
|
||
|
|
sb.WriteString("}\n")
|
||
|
|
|
||
|
|
return sb.String()
|
||
|
|
}
|
||
|
|
|
||
|
|
func formatExport(name string, val any) string {
|
||
|
|
t := reflect.TypeOf(val)
|
||
|
|
if t == nil {
|
||
|
|
return fmt.Sprintf("const %s: any;", name)
|
||
|
|
}
|
||
|
|
|
||
|
|
if t.Kind() == reflect.Func {
|
||
|
|
return fmt.Sprintf("function %s%s;", name, formatFunc(t))
|
||
|
|
}
|
||
|
|
|
||
|
|
return fmt.Sprintf("const %s: %s;", name, goTypeToTS(t))
|
||
|
|
}
|
||
|
|
|
||
|
|
func formatFunc(t reflect.Type) string {
|
||
|
|
var params []string
|
||
|
|
numIn := t.NumIn()
|
||
|
|
startIdx := 0
|
||
|
|
// Skip context.Context in TS doc as it's injected automatically
|
||
|
|
if numIn > 0 && t.In(0).String() == "context.Context" {
|
||
|
|
startIdx = 1
|
||
|
|
}
|
||
|
|
|
||
|
|
for i := startIdx; i < numIn; i++ {
|
||
|
|
params = append(params, fmt.Sprintf("arg%d: %s", i-startIdx, goTypeToTS(t.In(i))))
|
||
|
|
}
|
||
|
|
|
||
|
|
// Handle return values
|
||
|
|
numOut := t.NumOut()
|
||
|
|
var retType string
|
||
|
|
if numOut == 0 {
|
||
|
|
retType = "void"
|
||
|
|
} else {
|
||
|
|
// If last return is error, we only care about the first part for TS doc
|
||
|
|
realOut := numOut
|
||
|
|
if numOut > 0 && t.Out(numOut-1).String() == "error" {
|
||
|
|
realOut--
|
||
|
|
}
|
||
|
|
|
||
|
|
if realOut <= 0 {
|
||
|
|
retType = "void"
|
||
|
|
} else if realOut == 1 {
|
||
|
|
retType = goTypeToTS(t.Out(0))
|
||
|
|
} else {
|
||
|
|
retType = "any[]"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return fmt.Sprintf("(%s): %s", strings.Join(params, ", "), retType)
|
||
|
|
}
|
||
|
|
|
||
|
|
func goTypeToTS(t reflect.Type) string {
|
||
|
|
if t == nil {
|
||
|
|
return "any"
|
||
|
|
}
|
||
|
|
|
||
|
|
// Handle pointers
|
||
|
|
for t.Kind() == reflect.Ptr {
|
||
|
|
t = t.Elem()
|
||
|
|
}
|
||
|
|
|
||
|
|
switch t.Kind() {
|
||
|
|
case reflect.String:
|
||
|
|
return "string"
|
||
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||
|
|
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
||
|
|
reflect.Float32, reflect.Float64:
|
||
|
|
return "number"
|
||
|
|
case reflect.Bool:
|
||
|
|
return "boolean"
|
||
|
|
case reflect.Slice, reflect.Array:
|
||
|
|
return goTypeToTS(t.Elem()) + "[]"
|
||
|
|
case reflect.Map:
|
||
|
|
return "Record<string, any>"
|
||
|
|
case reflect.Struct:
|
||
|
|
// For structs, we could recursively list fields, but for a concise AI doc,
|
||
|
|
// "any" or the struct name is often sufficient.
|
||
|
|
// Let's at least show it's an object.
|
||
|
|
return "{ [key: string]: any }"
|
||
|
|
case reflect.Interface:
|
||
|
|
return "any"
|
||
|
|
default:
|
||
|
|
return "any"
|
||
|
|
}
|
||
|
|
}
|