mirror of
https://github.com/wailsapp/wails.git
synced 2026-03-14 14:45:49 +01:00
Create bindings file per package
Improved bindings tests
This commit is contained in:
parent
f1a7f1b781
commit
7340247e25
18 changed files with 286 additions and 167 deletions
|
|
@ -1,3 +1,7 @@
|
|||
// @ts-check
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
function GreetService(method) {
|
||||
return {
|
||||
packageName: "main",
|
||||
|
|
@ -9,17 +13,17 @@ function GreetService(method) {
|
|||
|
||||
/**
|
||||
* GreetService.Greet
|
||||
* Greet someone
|
||||
*
|
||||
* @param name {string}
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
**/
|
||||
function Greet(name) {
|
||||
return wails.Call(GreetService("Greet", name));
|
||||
}
|
||||
|
||||
window.go = window.go || {};
|
||||
Object.window.go.main = {
|
||||
window.go.main = {
|
||||
GreetService: {
|
||||
Greet,
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
@ -1,34 +1,15 @@
|
|||
package commands
|
||||
|
||||
import "github.com/wailsapp/wails/v3/internal/parser"
|
||||
|
||||
type GenerateBindingsOptions struct {
|
||||
Silent bool `name:"silent" description:"Silent mode"`
|
||||
ModelsFilename string `name:"m" description:"The filename for the models file" default:"models.ts"`
|
||||
BindingsFilename string `name:"b" description:"The filename for the bindings file" default:"bindings.js"`
|
||||
ProjectDirectory string `name:"d" description:"The project directory" default:"."`
|
||||
ProjectDirectory string `name:"p" description:"The project directory" default:"."`
|
||||
OutputDirectory string `name:"d" description:"The output directory" default:"."`
|
||||
}
|
||||
|
||||
func GenerateBindings(options *GenerateBindingsOptions) error {
|
||||
|
||||
// parserContext, err := parser.ParseDirectory(options.ProjectDirectory)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("error parsing project: %v", err)
|
||||
// }
|
||||
//
|
||||
// // Generate models
|
||||
// modelsData, err := parser.GenerateModels(parserContext)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("error generating models: %v", err)
|
||||
// }
|
||||
//
|
||||
// var modelsFileData bytes.Buffer
|
||||
// modelsFileData.WriteString(`// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
//// This file is automatically generated. DO NOT EDIT
|
||||
//
|
||||
//`)
|
||||
// modelsFileData.Write(modelsData)
|
||||
// err = os.WriteFile(options.ModelsFilename, modelsFileData.Bytes(), 0755)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("error writing models file: %v", err)
|
||||
// }
|
||||
// println("Generated models file '" + options.ModelsFilename + "'")
|
||||
return nil
|
||||
return parser.GenerateBindingsAndModels(options.ProjectDirectory, options.OutputDirectory)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -153,70 +153,66 @@ func normalisePackageNames(packageNames []string) map[string]string {
|
|||
return result
|
||||
}
|
||||
|
||||
func GenerateBindings(bindings map[string]map[string][]*BoundMethod) string {
|
||||
func GenerateBindings(bindings map[string]map[string][]*BoundMethod) map[string]string {
|
||||
|
||||
var result = make(map[string]string)
|
||||
|
||||
var result string
|
||||
var allModels []string
|
||||
var normalisedPackageNames = normalisePackageNames(lo.Keys(bindings))
|
||||
// sort the bindings keys
|
||||
packageNames := lo.Keys(bindings)
|
||||
sort.Strings(packageNames)
|
||||
for _, packageName := range packageNames {
|
||||
var allModels []string
|
||||
|
||||
packageBindings := bindings[packageName]
|
||||
structNames := lo.Keys(packageBindings)
|
||||
sort.Strings(structNames)
|
||||
for _, structName := range structNames {
|
||||
result += GenerateHelper(normalisedPackageNames[packageName], structName)
|
||||
result[normalisedPackageNames[packageName]] += GenerateHelper(normalisedPackageNames[packageName], structName)
|
||||
methods := packageBindings[structName]
|
||||
sort.Slice(methods, func(i, j int) bool {
|
||||
return methods[i].Name < methods[j].Name
|
||||
})
|
||||
for _, method := range methods {
|
||||
thisBinding, models := GenerateBinding(structName, method)
|
||||
result += thisBinding
|
||||
result[normalisedPackageNames[packageName]] += thisBinding
|
||||
allModels = append(allModels, models...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result += `
|
||||
result[normalisedPackageNames[packageName]] += `
|
||||
window.go = window.go || {};
|
||||
`
|
||||
// Iterate over the sorted bindings keys
|
||||
packageNames = lo.Keys(bindings)
|
||||
for _, packageName := range packageNames {
|
||||
packageBindings := bindings[packageName]
|
||||
structNames := lo.Keys(packageBindings)
|
||||
sort.Strings(structNames)
|
||||
result += "window.go." + normalisedPackageNames[packageName] + " = {\n"
|
||||
// Iterate over the sorted struct keys
|
||||
result[normalisedPackageNames[packageName]] += "window.go." + normalisedPackageNames[packageName] + " = {\n"
|
||||
for _, structName := range structNames {
|
||||
result += " " + structName + ": {\n"
|
||||
result[normalisedPackageNames[packageName]] += " " + structName + ": {\n"
|
||||
methods := packageBindings[structName]
|
||||
sort.Slice(methods, func(i, j int) bool {
|
||||
return methods[i].Name < methods[j].Name
|
||||
})
|
||||
for _, method := range methods {
|
||||
result += " " + method.Name + ",\n"
|
||||
result[normalisedPackageNames[packageName]] += " " + method.Name + ",\n"
|
||||
}
|
||||
result += " },\n"
|
||||
result[normalisedPackageNames[packageName]] += " },\n"
|
||||
}
|
||||
result += "};\n"
|
||||
}
|
||||
result[normalisedPackageNames[packageName]] += "};\n"
|
||||
|
||||
// add imports
|
||||
if len(allModels) > 0 {
|
||||
allModels := lo.Uniq(allModels)
|
||||
var models []string
|
||||
for _, model := range allModels {
|
||||
models = append(models, normalisedPackageNames[model])
|
||||
// add imports
|
||||
if len(allModels) > 0 {
|
||||
allModels := lo.Uniq(allModels)
|
||||
var models []string
|
||||
for _, model := range allModels {
|
||||
models = append(models, normalisedPackageNames[model])
|
||||
}
|
||||
sort.Strings(models)
|
||||
result[normalisedPackageNames[packageName]] += "\n"
|
||||
imports := "import {" + strings.Join(models, ", ") + "} from './models';\n"
|
||||
result[normalisedPackageNames[packageName]] = imports + "\n" + result[normalisedPackageNames[packageName]]
|
||||
}
|
||||
sort.Strings(models)
|
||||
result += "\n"
|
||||
imports := "import {" + strings.Join(models, ", ") + "} from './models';\n"
|
||||
result = imports + "\n" + result
|
||||
}
|
||||
|
||||
result = header + result
|
||||
result[normalisedPackageNames[packageName]] = header + result[normalisedPackageNames[packageName]]
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,30 +1,94 @@
|
|||
package parser
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"io/fs"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
//go:embed testdata
|
||||
var testdata embed.FS
|
||||
|
||||
func getFile(filename string) string {
|
||||
// get the file from the testdata FS
|
||||
file, err := fs.ReadFile(testdata, filename)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return string(file)
|
||||
}
|
||||
|
||||
func TestGenerateBindings(t *testing.T) {
|
||||
|
||||
tests := []string{
|
||||
"struct_literal_single",
|
||||
"struct_literal_multiple",
|
||||
"struct_literal_multiple_other",
|
||||
"struct_literal_multiple_files",
|
||||
"function_single",
|
||||
"function_from_imported_package",
|
||||
"variable_single",
|
||||
"variable_single_from_function",
|
||||
"variable_single_from_other_function",
|
||||
tests := []struct {
|
||||
dir string
|
||||
want map[string]string
|
||||
}{
|
||||
{
|
||||
"testdata/function_single",
|
||||
map[string]string{
|
||||
"main": getFile("testdata/function_single/bindings_main.js"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"testdata/function_from_imported_package",
|
||||
map[string]string{
|
||||
"main": getFile("testdata/function_from_imported_package/bindings_main.js"),
|
||||
"services": getFile("testdata/function_from_imported_package/bindings_services.js"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"testdata/variable_single",
|
||||
map[string]string{
|
||||
"main": getFile("testdata/variable_single/bindings_main.js"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"testdata/variable_single_from_function",
|
||||
map[string]string{
|
||||
"main": getFile("testdata/variable_single_from_function/bindings_main.js"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"testdata/variable_single_from_other_function",
|
||||
map[string]string{
|
||||
"main": getFile("testdata/variable_single_from_other_function/bindings_main.js"),
|
||||
"services": getFile("testdata/variable_single_from_other_function/bindings_services.js"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"testdata/struct_literal_single",
|
||||
map[string]string{
|
||||
"main": getFile("testdata/struct_literal_single/bindings_main.js"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"testdata/struct_literal_multiple",
|
||||
map[string]string{
|
||||
"main": getFile("testdata/struct_literal_multiple/bindings_main.js"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"testdata/struct_literal_multiple_other",
|
||||
map[string]string{
|
||||
"main": getFile("testdata/struct_literal_multiple_other/bindings_main.js"),
|
||||
"services": getFile("testdata/struct_literal_multiple_other/bindings_services.js"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"testdata/struct_literal_multiple_files",
|
||||
map[string]string{
|
||||
"main": getFile("testdata/struct_literal_multiple_files/bindings_main.js"),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, projectDir := range tests {
|
||||
t.Run(projectDir, func(t *testing.T) {
|
||||
projectDir = "testdata/" + projectDir
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.dir, func(t *testing.T) {
|
||||
// Run parser on directory
|
||||
project, err := ParseProject(projectDir)
|
||||
project, err := ParseProject(tt.dir)
|
||||
if err != nil {
|
||||
t.Errorf("ParseProject() error = %v", err)
|
||||
return
|
||||
|
|
@ -33,28 +97,27 @@ func TestGenerateBindings(t *testing.T) {
|
|||
// Generate Bindings
|
||||
got := GenerateBindings(project.BoundMethods)
|
||||
|
||||
// Load bindings.js from project directory
|
||||
expected, err := os.ReadFile(projectDir + "/bindings.js")
|
||||
if err != nil {
|
||||
// Write file to project directory
|
||||
err = os.WriteFile(projectDir+"/bindings.got.js", []byte(got), 0644)
|
||||
if err != nil {
|
||||
t.Errorf("os.WriteFile() error = %v", err)
|
||||
for name, binding := range got {
|
||||
// check if the binding is in the expected bindings
|
||||
expected, ok := tt.want[name]
|
||||
if !ok {
|
||||
err = os.WriteFile(tt.dir+"/bindings_"+name+".got.js", []byte(binding), 0644)
|
||||
if err != nil {
|
||||
t.Errorf("os.WriteFile() error = %v", err)
|
||||
return
|
||||
}
|
||||
t.Errorf("GenerateBindings() unexpected binding = %v", name)
|
||||
return
|
||||
}
|
||||
t.Errorf("os.ReadFile() error = %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Compare
|
||||
if diff := cmp.Diff(string(expected), got); diff != "" {
|
||||
// Write file to project directory
|
||||
err = os.WriteFile(projectDir+"/bindings.got.js", []byte(got), 0644)
|
||||
if err != nil {
|
||||
t.Errorf("os.WriteFile() error = %v", err)
|
||||
return
|
||||
// compare the binding
|
||||
if diff := cmp.Diff(expected, binding); diff != "" {
|
||||
err = os.WriteFile(tt.dir+"/bindings_"+name+".got.js", []byte(binding), 0644)
|
||||
if err != nil {
|
||||
t.Errorf("os.WriteFile() error = %v", err)
|
||||
return
|
||||
}
|
||||
t.Fatalf("GenerateBindings() mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
t.Fatalf("GenerateService() mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,10 @@ func GenerateModel(wr io.Writer, def *ModelDefinitions) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func GenerateModels(models map[packagePath]map[structName]*StructDef) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
//func GenerateClass(wr io.Writer, def *StructDef) error {
|
||||
// tmpl, err := template.New("class.ts.tmpl").ParseFiles("templates/class.ts.tmpl")
|
||||
// if err != nil {
|
||||
|
|
|
|||
|
|
@ -126,7 +126,6 @@ func ParseProject(projectPath string) (*Project, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
println("Parsed " + projectPath)
|
||||
err = result.findApplicationNewCalls(pkgs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -142,6 +141,51 @@ func ParseProject(projectPath string) (*Project, error) {
|
|||
return result, nil
|
||||
}
|
||||
|
||||
func GenerateBindingsAndModels(projectDir string, outputDir string) error {
|
||||
p, err := ParseProject(projectDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.BoundMethods == nil {
|
||||
return nil
|
||||
}
|
||||
err = os.MkdirAll(outputDir, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
generatedMethods := GenerateBindings(p.BoundMethods)
|
||||
for pkg, text := range generatedMethods {
|
||||
// Write the file
|
||||
err = os.WriteFile(filepath.Join(outputDir, "bindings_"+pkg+".js"), []byte(text), 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Generate Models
|
||||
if len(p.Models) > 0 {
|
||||
generatedModels := GenerateModels(p.Models)
|
||||
err = os.WriteFile(filepath.Join(outputDir, "models.js"), []byte(generatedModels), 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
absPath, err := filepath.Abs(projectDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
println("Generated bindings and models for project: " + absPath)
|
||||
absPath, err = filepath.Abs(outputDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
println("Output directory: " + absPath)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Project) parseDirectory(dir string) (map[string]*ParsedPackage, error) {
|
||||
if p.packageCache[dir] != nil {
|
||||
return map[string]*ParsedPackage{dir: p.packageCache[dir]}, nil
|
||||
|
|
|
|||
|
|
@ -2,26 +2,8 @@
|
|||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
import {main, services} from './models';
|
||||
import {main} from './models';
|
||||
|
||||
function OtherService(method) {
|
||||
return {
|
||||
packageName: "services",
|
||||
serviceName: "OtherService",
|
||||
methodName: method,
|
||||
args: Array.prototype.slice.call(arguments, 1),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* OtherService.Yay
|
||||
*
|
||||
*
|
||||
* @returns {Promise<services.Address | null>}
|
||||
**/
|
||||
function Yay() {
|
||||
return wails.Call(OtherService("Yay"));
|
||||
}
|
||||
function GreetService(method) {
|
||||
return {
|
||||
packageName: "main",
|
||||
|
|
@ -58,9 +40,4 @@ window.go.main = {
|
|||
NewPerson,
|
||||
},
|
||||
};
|
||||
window.go.services = {
|
||||
OtherService: {
|
||||
Yay,
|
||||
},
|
||||
};
|
||||
|
||||
32
v3/internal/parser/testdata/function_from_imported_package/bindings_services.js
vendored
Normal file
32
v3/internal/parser/testdata/function_from_imported_package/bindings_services.js
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
// @ts-check
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
import {services} from './models';
|
||||
|
||||
function OtherService(method) {
|
||||
return {
|
||||
packageName: "services",
|
||||
serviceName: "OtherService",
|
||||
methodName: method,
|
||||
args: Array.prototype.slice.call(arguments, 1),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* OtherService.Yay
|
||||
*
|
||||
*
|
||||
* @returns {Promise<services.Address | null>}
|
||||
**/
|
||||
function Yay() {
|
||||
return wails.Call(OtherService("Yay"));
|
||||
}
|
||||
|
||||
window.go = window.go || {};
|
||||
window.go.services = {
|
||||
OtherService: {
|
||||
Yay,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -2,26 +2,8 @@
|
|||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
import {main, services} from './models';
|
||||
import {main} from './models';
|
||||
|
||||
function OtherService(method) {
|
||||
return {
|
||||
packageName: "services",
|
||||
serviceName: "OtherService",
|
||||
methodName: method,
|
||||
args: Array.prototype.slice.call(arguments, 1),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* OtherService.Yay
|
||||
*
|
||||
*
|
||||
* @returns {Promise<services.Address | null>}
|
||||
**/
|
||||
function Yay() {
|
||||
return wails.Call(OtherService("Yay"));
|
||||
}
|
||||
function GreetService(method) {
|
||||
return {
|
||||
packageName: "main",
|
||||
|
|
@ -58,9 +40,4 @@ window.go.main = {
|
|||
NewPerson,
|
||||
},
|
||||
};
|
||||
window.go.services = {
|
||||
OtherService: {
|
||||
Yay,
|
||||
},
|
||||
};
|
||||
|
||||
32
v3/internal/parser/testdata/struct_literal_multiple_other/bindings_services.js
vendored
Normal file
32
v3/internal/parser/testdata/struct_literal_multiple_other/bindings_services.js
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
// @ts-check
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
import {services} from './models';
|
||||
|
||||
function OtherService(method) {
|
||||
return {
|
||||
packageName: "services",
|
||||
serviceName: "OtherService",
|
||||
methodName: method,
|
||||
args: Array.prototype.slice.call(arguments, 1),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* OtherService.Yay
|
||||
*
|
||||
*
|
||||
* @returns {Promise<services.Address | null>}
|
||||
**/
|
||||
function Yay() {
|
||||
return wails.Call(OtherService("Yay"));
|
||||
}
|
||||
|
||||
window.go = window.go || {};
|
||||
window.go.services = {
|
||||
OtherService: {
|
||||
Yay,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -2,26 +2,8 @@
|
|||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
import {main, services} from './models';
|
||||
import {main} from './models';
|
||||
|
||||
function OtherService(method) {
|
||||
return {
|
||||
packageName: "services",
|
||||
serviceName: "OtherService",
|
||||
methodName: method,
|
||||
args: Array.prototype.slice.call(arguments, 1),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* OtherService.Yay
|
||||
*
|
||||
*
|
||||
* @returns {Promise<services.Address | null>}
|
||||
**/
|
||||
function Yay() {
|
||||
return wails.Call(OtherService("Yay"));
|
||||
}
|
||||
function GreetService(method) {
|
||||
return {
|
||||
packageName: "main",
|
||||
|
|
@ -58,9 +40,4 @@ window.go.main = {
|
|||
NewPerson,
|
||||
},
|
||||
};
|
||||
window.go.services = {
|
||||
OtherService: {
|
||||
Yay,
|
||||
},
|
||||
};
|
||||
|
||||
32
v3/internal/parser/testdata/variable_single_from_other_function/bindings_services.js
vendored
Normal file
32
v3/internal/parser/testdata/variable_single_from_other_function/bindings_services.js
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
// @ts-check
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
import {services} from './models';
|
||||
|
||||
function OtherService(method) {
|
||||
return {
|
||||
packageName: "services",
|
||||
serviceName: "OtherService",
|
||||
methodName: method,
|
||||
args: Array.prototype.slice.call(arguments, 1),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* OtherService.Yay
|
||||
*
|
||||
*
|
||||
* @returns {Promise<services.Address | null>}
|
||||
**/
|
||||
function Yay() {
|
||||
return wails.Call(OtherService("Yay"));
|
||||
}
|
||||
|
||||
window.go = window.go || {};
|
||||
window.go.services = {
|
||||
OtherService: {
|
||||
Yay,
|
||||
},
|
||||
};
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue