mirror of
https://github.com/wailsapp/wails.git
synced 2026-03-14 14:45:49 +01:00
Support struct discovery over multiple files
This commit is contained in:
parent
b0b96c9838
commit
0a09363db7
2 changed files with 142 additions and 2 deletions
|
|
@ -1,10 +1,12 @@
|
|||
package parser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"strings"
|
||||
|
||||
"github.com/samber/lo"
|
||||
|
|
@ -30,6 +32,16 @@ type Context struct {
|
|||
packages map[string]*parsedPackage
|
||||
}
|
||||
|
||||
func (c *Context) GetBoundStructs() map[string][]*ast.TypeSpec {
|
||||
structs := make(map[string][]*ast.TypeSpec)
|
||||
for _, pkg := range c.packages {
|
||||
for _, structType := range pkg.boundStructs {
|
||||
structs[pkg.name] = append(structs[pkg.name], structType)
|
||||
}
|
||||
}
|
||||
return structs
|
||||
}
|
||||
|
||||
func ParseDirectory(dir string) (*Context, error) {
|
||||
// Parse the directory
|
||||
fset := token.NewFileSet()
|
||||
|
|
@ -150,7 +162,9 @@ func findApplicationNewCalls(context *Context) {
|
|||
ident, ok := boundStructLit.Type.(*ast.Ident)
|
||||
if ok {
|
||||
if ident.Obj == nil {
|
||||
debug("Ident.Obj is nil - check")
|
||||
structTypeSpec := findStructInPackage(thisPackage.pkg, ident.Name)
|
||||
thisPackage.boundStructs[ident.Name] = structTypeSpec
|
||||
findNestedStructs(structTypeSpec, file, packageName, context)
|
||||
continue
|
||||
}
|
||||
// Check if the ident is a struct type
|
||||
|
|
@ -384,3 +398,80 @@ func parseField(field *ast.Field, parsedFile *ast.File, pkgName string, context
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func findStructInPackage(pkg *ast.Package, name string) *ast.TypeSpec {
|
||||
for _, file := range pkg.Files {
|
||||
for _, decl := range file.Decls {
|
||||
if gen, ok := decl.(*ast.GenDecl); ok && gen.Tok == token.TYPE {
|
||||
for _, spec := range gen.Specs {
|
||||
if typeSpec, ok := spec.(*ast.TypeSpec); ok {
|
||||
if typeSpec.Name.Name == name {
|
||||
if _, ok := typeSpec.Type.(*ast.StructType); ok {
|
||||
return typeSpec
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var goToTS = map[string]string{
|
||||
"int": "number",
|
||||
"int8": "number",
|
||||
"int16": "number",
|
||||
"int32": "number",
|
||||
"int64": "number",
|
||||
"uint": "number",
|
||||
"uint8": "number",
|
||||
"uint16": "number",
|
||||
"uint32": "number",
|
||||
"uint64": "number",
|
||||
"float32": "number",
|
||||
"float64": "number",
|
||||
"string": "string",
|
||||
"bool": "boolean",
|
||||
}
|
||||
|
||||
func GenerateTypeScript(specs map[string][]*ast.TypeSpec) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
for pkg, pkgSpecs := range specs {
|
||||
if _, err := fmt.Fprintf(&buf, "namespace %s {\n", pkg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, spec := range pkgSpecs {
|
||||
if structType, ok := spec.Type.(*ast.StructType); ok {
|
||||
if _, err := fmt.Fprintf(&buf, " class %s {\n", spec.Name.Name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, field := range structType.Fields.List {
|
||||
// Get the Go type of the field
|
||||
goType := types.ExprString(field.Type)
|
||||
// Look up the corresponding TypeScript type
|
||||
tsType, ok := goToTS[goType]
|
||||
if !ok {
|
||||
tsType = goType
|
||||
}
|
||||
|
||||
if _, err := fmt.Fprintf(&buf, " %s: %s;\n", field.Names[0].Name, tsType); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := fmt.Fprintf(&buf, " }\n"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := fmt.Fprintf(&buf, "}\n"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/samber/lo"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
|
@ -17,7 +18,7 @@ func TestParseDirectory(t *testing.T) {
|
|||
{
|
||||
name: "should find single bound service",
|
||||
dir: "testdata/struct_literal_single",
|
||||
want: []string{"GreetService"},
|
||||
want: []string{"main.GreetService"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
|
|
@ -55,6 +56,54 @@ func TestParseDirectory(t *testing.T) {
|
|||
}
|
||||
}
|
||||
require.Empty(t, tt.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateTypeScript(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
dir string
|
||||
want string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "should find single bound service",
|
||||
dir: "testdata/struct_literal_single",
|
||||
want: "",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "should find multiple bound services",
|
||||
dir: "testdata/struct_literal_multiple",
|
||||
want: "",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "should find multiple bound services over multiple files",
|
||||
dir: "testdata/struct_literal_multiple_files",
|
||||
want: "",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "should find bound services from other packages",
|
||||
dir: "../../examples/binding",
|
||||
want: "",
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
Debug = true
|
||||
context, err := ParseDirectory(tt.dir)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ParseDirectory() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
|
||||
ts, err := GenerateTypeScript(context.GetBoundStructs())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, ts, tt.want)
|
||||
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue