5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-02 19:50:15 +08:00

Initial bindings.js generation

This commit is contained in:
Lea Anthony 2023-03-03 19:54:12 +11:00
parent 868b769e7f
commit 71aa7c9731
No known key found for this signature in database
GPG Key ID: 33DAF7BB90A58405
3 changed files with 123 additions and 71 deletions

View File

@ -2,6 +2,8 @@ package parser
import (
"strings"
"github.com/samber/lo"
)
const helperTemplate = `function {{structName}}(method) {
@ -11,7 +13,8 @@ const helperTemplate = `function {{structName}}(method) {
methodName: method,
args: Array.prototype.slice.call(arguments, 1),
};
}`
}
`
func GenerateHelper(packageName, structName string) string {
result := strings.ReplaceAll(helperTemplate, "{{packageName}}", packageName)
@ -20,45 +23,109 @@ func GenerateHelper(packageName, structName string) string {
}
const bindingTemplate = `
/**
* {{structName}}.{{methodName}}
* Comments
* @param name {string}
* @returns {Promise<string>}
*/
function {{methodName}}({{args}}) {
return wails.Call({{structName}}("{{methodName}}", {{args}}));
**/
function {{methodName}}({{inputs}}) {
return wails.Call({{structName}}("{{methodName}}"{{args}}));
}
`
func GenerateBinding(structName string, method *BoundMethod) string {
func sanitiseJSVarName(name string) string {
// if the name is a reserved word, prefix with an
// underscore
if strings.Contains("break,case,catch,class,const,continue,debugger,default,delete,do,else,enum,export,extends,false,finally,for,function,if,implements,import,in,instanceof,interface,let,new,null,package,private,protected,public,return,static,super,switch,this,throw,true,try,typeof,var,void,while,with,yield", name) {
return "_" + name
}
return name
}
func GenerateBinding(structName string, method *BoundMethod) (string, []string) {
var models []string
result := strings.ReplaceAll(bindingTemplate, "{{structName}}", structName)
result = strings.ReplaceAll(result, "{{methodName}}", method.Name)
result = strings.ReplaceAll(result, "Comments", strings.TrimSpace(method.DocComment))
comments := strings.TrimSpace(method.DocComment)
result = strings.ReplaceAll(result, "Comments", comments)
var params string
for _, input := range method.Inputs {
params += " * @param " + input.Name + " {" + input.JSType() + "}\n"
pkgName := getPackageName(input)
if pkgName != "" {
models = append(models, pkgName)
}
params += " * @param " + sanitiseJSVarName(input.Name) + " {" + input.JSType() + "}\n"
}
params = strings.TrimSuffix(params, "\n")
result = strings.ReplaceAll(result, " * @param name {string}", params)
var args string
for _, input := range method.Inputs {
args += input.Name + ", "
if len(params) == 0 {
params = " *"
}
args = strings.TrimSuffix(args, ", ")
////params += "\n"
result = strings.ReplaceAll(result, " * @param name {string}", params)
var inputs string
for _, input := range method.Inputs {
pkgName := getPackageName(input)
if pkgName != "" {
models = append(models, pkgName)
}
inputs += sanitiseJSVarName(input.Name) + ", "
}
inputs = strings.TrimSuffix(inputs, ", ")
args := inputs
if len(args) > 0 {
args = ", " + args
}
result = strings.ReplaceAll(result, "{{inputs}}", inputs)
result = strings.ReplaceAll(result, "{{args}}", args)
// outputs
var returns string
if len(method.Outputs) == 0 {
returns = " * @returns {Promise<void>}"
} else {
returns = " * @returns {Promise<"
for _, output := range method.Outputs {
pkgName := getPackageName(output)
if pkgName != "" {
models = append(models, pkgName)
}
jsType := output.JSType()
if jsType == "error" {
jsType = "void"
}
returns += jsType + ", "
}
returns = strings.TrimSuffix(returns, ", ")
returns += ">}"
}
result = strings.ReplaceAll(result, " * @returns {Promise<string>}", returns)
return result, lo.Uniq(models)
}
func getPackageName(input *Parameter) string {
if !input.Type.IsStruct {
return ""
}
result := input.Type.Package
if result == "" {
result = "main"
}
return result
}
func GenerateBindings(bindings map[string]map[string][]*BoundMethod) string {
var result string
var allModels []string
for packageName, packageBindings := range bindings {
for structName, bindings := range packageBindings {
result += GenerateHelper(packageName, structName)
for _, binding := range bindings {
result += GenerateBinding(structName, binding)
thisBinding, models := GenerateBinding(structName, binding)
result += thisBinding
allModels = append(allModels, models...)
}
}
}
@ -76,5 +143,10 @@ window.go = window.go || {};
}
result += "};\n"
}
// add imports
imports := "import {" + strings.Join(lo.Uniq(allModels), ", ") + "} from './models';\n"
result = imports + "\n" + result
return result
}

View File

@ -1,66 +1,46 @@
package parser
import (
"github.com/google/go-cmp/cmp"
"os"
"testing"
"github.com/google/go-cmp/cmp"
)
const expectedGreetService = `function GreetService(method) {
return {
packageName: "main",
serviceName: "GreetService",
methodName: method,
args: Array.prototype.slice.call(arguments, 1),
};
}
func TestGenerateBindings(t *testing.T) {
/**
* 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 = {
GreetService: {
Greet,
tests := []string{
"struct_literal_single",
}
};
`
func TestGenerateGreetService(t *testing.T) {
parsedMethods := map[string]map[string][]*BoundMethod{
"main": {
"GreetService": {
{
Name: "Greet",
DocComment: "Greet someone\n",
Inputs: []*Parameter{
{
Name: "name",
Type: &ParameterType{
Name: "string",
},
},
},
Outputs: []*Parameter{
{
Name: "",
Type: &ParameterType{
Name: "string",
},
},
},
},
},
},
for _, projectDir := range tests {
t.Run(projectDir, func(t *testing.T) {
projectDir = "testdata/" + projectDir
// Run parser on directory
project, err := ParseProject(projectDir)
if err != nil {
t.Errorf("ParseProject() error = %v", err)
return
}
got := GenerateBindings(parsedMethods)
if diff := cmp.Diff(expectedGreetService, got); diff != "" {
// Generate Bindings
got := GenerateBindings(project.BoundMethods)
// 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
}
// Load bindings.js from project directory
expected, err := os.ReadFile(projectDir + "/bindings.js")
if err != nil {
t.Errorf("os.ReadFile() error = %v", err)
return
}
// Compare
if diff := cmp.Diff(string(expected), got); diff != "" {
t.Fatalf("GenerateService() mismatch (-want +got):\n%s", diff)
}
})
}
}

View File

@ -42,7 +42,7 @@ func (p *Parameter) JSType() string {
// Convert type to javascript equivalent type
var typeName string
switch p.Type.Name {
case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "uintptr":
case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "uintptr", "float32", "float64":
typeName = "number"
case "string":
typeName = "string"