mirror of
https://github.com/wavetermdev/wails.git
synced 2026-08-05 13:53:43 -07:00
Update bindings generator to generate bindings in packages and files.
Remove unused JavaScript files Update tests. Update v3 docs
This commit is contained in:
@@ -17,21 +17,16 @@ We also want to get all examples working on Linux.
|
||||
|
||||
#### How Can I Help?
|
||||
|
||||
If you are interested in helping out, please review the table below and look for
|
||||
untested scenarios. The parser code and tests are located in `v3/internal/parser`.
|
||||
All tests can be run using `go test ./...` from the `v3` directory.
|
||||
You can generate bindings using the `wails3 generate bindings` command. This will generate bindings for all exported struct methods bound to your project.
|
||||
You can then use these bindings in your frontend code. You can see an example of this in the `examples/bindings` directory.
|
||||
Run `wails3 generate bindings -help` to view options that govern how bindings are generated.
|
||||
|
||||
Review the table below and look for untested scenarios. The parser code and tests are located in `v3/internal/parser`. All tests can be run using `go test ./...` from the `v3` directory.
|
||||
Basically, try to break it and let us know if you find any issues! :smile:
|
||||
|
||||
#### Status
|
||||
|
||||
Bindings for struct (CallByID):
|
||||
- [x] Same package
|
||||
- [x] Different package
|
||||
- [ ] Different package with same name
|
||||
- [x] Containing another struct from same package
|
||||
- [x] Containing another struct from different package
|
||||
- [x] Containing an anonymous struct
|
||||
|
||||
|
||||
- :material-check-bold: - Working
|
||||
- :material-minus: - Partially working
|
||||
@@ -40,12 +35,6 @@ Bindings for struct (CallByID):
|
||||
{{ read_csv("alpha3-bindings-callbyid.csv") }}
|
||||
|
||||
Bindings for struct (CallByName):
|
||||
- [ ] Same package
|
||||
- [ ] Different package
|
||||
- [ ] Different package with same name
|
||||
- [ ] Containing another struct from same package
|
||||
- [ ] Containing another struct from different package
|
||||
- [ ] Containing an anonymous struct
|
||||
|
||||
- :material-check-bold: - Working
|
||||
- :material-minus: - Partially working
|
||||
@@ -54,32 +43,18 @@ Bindings for struct (CallByName):
|
||||
{{ read_csv("alpha3-bindings-callbyname.csv") }}
|
||||
|
||||
Models:
|
||||
- [x] Class model for struct in same package
|
||||
- [x] Class model for struct in different package
|
||||
- [ ] Interface model for struct in same package
|
||||
- [ ] Interface model for struct in different package
|
||||
- [x] Enum in same package
|
||||
- [x] Enum in different package
|
||||
- [x] Interface using enum in same package
|
||||
- [ ] Interface using enum in different package
|
||||
|
||||
Examples:
|
||||
- [ ] All examples working on Linux
|
||||
|
||||
|
||||
- :material-check-bold: - Working
|
||||
- :material-minus: - Partially working
|
||||
- :material-close: - Not working
|
||||
|
||||
**Bindings**:
|
||||
|
||||
{{ read_csv("alpha3-bindings-callbyid.csv") }}
|
||||
|
||||
**Models**:
|
||||
|
||||
{{ read_csv("alpha3-models.csv") }}
|
||||
|
||||
|
||||
Examples:
|
||||
|
||||
- [ ] All examples working on Linux
|
||||
|
||||
|
||||
### Alpha 2
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Scenario,Windows,Mac,Linux,WSL
|
||||
Same package," "," "," "," "
|
||||
Different package," "," "," "," "
|
||||
Same package,:material-check-bold:," "," "," "
|
||||
Different package,:material-check-bold:," "," "," "
|
||||
Different package with same name," "," "," "," "
|
||||
Containing another struct from same package," "," "," "," "
|
||||
Containing another struct from different package," "," "," "," "
|
||||
Containing an anonymous struct," "," "," "," "
|
||||
Containing another struct from same package,:material-check-bold:," "," "," "
|
||||
Containing another struct from different package,:material-check-bold:," "," "," "
|
||||
Containing an anonymous struct,:material-check-bold:," "," "," "
|
||||
|
||||
|
@@ -1,10 +1,10 @@
|
||||
Scenario,Windows,Mac,Linux,WSL
|
||||
Class model for struct in same package,:material-check-bold:," "," "," "
|
||||
Class model for struct in different package,:material-check-bold:," "," "," "
|
||||
Interface model for struct in same package," "," "," "," "
|
||||
Interface model for struct in different package," "," "," "," "
|
||||
Interface model for struct in same package,:material-check-bold:," "," "," "
|
||||
Interface model for struct in different package,:material-check-bold:," "," "," "
|
||||
Enum in same package,:material-check-bold:," "," "," "
|
||||
Enum in different package,:material-check-bold:," "," "," "
|
||||
Interface using enum in same package,:material-check-bold:," "," "," "
|
||||
Interface using enum in different package," "," "," "," "
|
||||
Interface using enum in different package,:material-check-bold:," "," "," "
|
||||
|
||||
|
||||
|
@@ -7,6 +7,7 @@ toolchain go1.21.4
|
||||
require (
|
||||
github.com/atterpac/refresh v0.2.4
|
||||
github.com/bep/debounce v1.2.1
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/ebitengine/purego v0.4.0-alpha.4
|
||||
github.com/go-git/go-git/v5 v5.3.0
|
||||
github.com/go-ole/go-ole v1.2.6
|
||||
|
||||
@@ -16,23 +16,26 @@ const header = `// @ts-check
|
||||
`
|
||||
|
||||
const bindingTemplate = `
|
||||
/**
|
||||
* {{structName}}.{{methodName}}
|
||||
*Comments
|
||||
* @param names {string}
|
||||
* @returns {Promise<string>}
|
||||
**/
|
||||
/**Comments
|
||||
* @function {{methodName}}* @param names {string}
|
||||
* @returns {Promise<string>}
|
||||
**/
|
||||
`
|
||||
|
||||
const callByID = ` {{methodName}}: function({{inputs}}) { return wails.CallByID({{ID}}, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
const callByID = `export function {{methodName}}({{inputs}}) {
|
||||
return wails.CallByID({{ID}}, ...Array.prototype.slice.call(arguments, 0));
|
||||
}
|
||||
`
|
||||
const callByName = ` {{methodName}}: function({{inputs}}) { return wails.CallByName("{{Name}}", ...Array.prototype.slice.call(arguments, 0)); },
|
||||
|
||||
const callByName = `export function {{methodName}}({{inputs}}) {
|
||||
return wails.CallByName("{{Name}}", ...Array.prototype.slice.call(arguments, 0));
|
||||
}
|
||||
`
|
||||
|
||||
const enumTemplate = `
|
||||
export enum {{.EnumName}} {
|
||||
{{.EnumValues}}
|
||||
}
|
||||
export enum {{.EnumName}} {
|
||||
{{.EnumValues}}
|
||||
}
|
||||
`
|
||||
|
||||
var reservedWords = []string{
|
||||
@@ -112,8 +115,13 @@ func sanitiseJSVarName(name string) string {
|
||||
return name
|
||||
}
|
||||
|
||||
func GenerateBinding(structName string, method *BoundMethod, useIDs bool) (string, []string, []string) {
|
||||
var namespacedStructs []string
|
||||
type ExternalStruct struct {
|
||||
Package string
|
||||
Name string
|
||||
}
|
||||
|
||||
func GenerateBinding(thisStructName string, method *BoundMethod, useIDs bool) (string, []string, map[packagePath]map[string]*ExternalStruct) {
|
||||
var externalStructs = make(map[packagePath]map[string]*ExternalStruct)
|
||||
var models []string
|
||||
template := bindingTemplate
|
||||
if useIDs {
|
||||
@@ -121,7 +129,7 @@ func GenerateBinding(structName string, method *BoundMethod, useIDs bool) (strin
|
||||
} else {
|
||||
template += callByName
|
||||
}
|
||||
result := strings.ReplaceAll(template, "{{structName}}", structName)
|
||||
result := strings.ReplaceAll(template, "{{structName}}", thisStructName)
|
||||
result = strings.ReplaceAll(result, "{{methodName}}", method.Name)
|
||||
result = strings.ReplaceAll(result, "{{ID}}", fmt.Sprintf("%v", method.ID))
|
||||
|
||||
@@ -129,10 +137,10 @@ func GenerateBinding(structName string, method *BoundMethod, useIDs bool) (strin
|
||||
parts := strings.Split(method.Package, "/")
|
||||
packageName := parts[len(parts)-1]
|
||||
|
||||
result = strings.ReplaceAll(result, "{{Name}}", fmt.Sprintf("%v.%v.%v", packageName, structName, method.Name))
|
||||
result = strings.ReplaceAll(result, "{{Name}}", fmt.Sprintf("%v.%v.%v", packageName, thisStructName, method.Name))
|
||||
comments := strings.TrimSpace(method.DocComment)
|
||||
if comments != "" {
|
||||
comments = " " + comments
|
||||
comments = "\n * " + comments
|
||||
}
|
||||
result = strings.ReplaceAll(result, "Comments", comments)
|
||||
var params string
|
||||
@@ -143,16 +151,22 @@ func GenerateBinding(structName string, method *BoundMethod, useIDs bool) (strin
|
||||
models = append(models, pkgName)
|
||||
}
|
||||
if input.Type.IsStruct || input.Type.IsEnum {
|
||||
nsStruct := input.NamespacedStructType()
|
||||
namespacedStructs = append(namespacedStructs, nsStruct)
|
||||
if _, ok := externalStructs[input.Type.Package]; !ok {
|
||||
externalStructs[input.Type.Package] = make(map[string]*ExternalStruct)
|
||||
}
|
||||
externalStructs[input.Type.Package][input.Type.Name] = &ExternalStruct{
|
||||
Package: input.Type.Package,
|
||||
Name: input.Type.Name,
|
||||
}
|
||||
}
|
||||
|
||||
params += " * @param " + inputName + " {" + input.JSType() + "}\n"
|
||||
inputType := input.JSType(packageName)
|
||||
params += "\n * @param " + inputName + " {" + inputType + "}"
|
||||
}
|
||||
params = strings.TrimSuffix(params, "\n")
|
||||
if len(params) == 0 {
|
||||
params = " *"
|
||||
}
|
||||
//if len(params) > 0 {
|
||||
// params = "\n" + params
|
||||
//}
|
||||
result = strings.ReplaceAll(result, "* @param names {string}", params)
|
||||
var inputs string
|
||||
for _, input := range method.Inputs {
|
||||
@@ -181,13 +195,19 @@ func GenerateBinding(structName string, method *BoundMethod, useIDs bool) (strin
|
||||
if pkgName != "" {
|
||||
models = append(models, pkgName)
|
||||
}
|
||||
jsType := output.JSType()
|
||||
jsType := output.JSType(packageName)
|
||||
if jsType == "error" {
|
||||
jsType = "void"
|
||||
}
|
||||
if output.Type.IsStruct {
|
||||
namespacedStructs = append(namespacedStructs, output.NamespacedStructType())
|
||||
jsType = output.NamespacedStructVariable()
|
||||
if _, ok := externalStructs[output.Type.Package]; !ok {
|
||||
externalStructs[output.Type.Package] = make(map[string]*ExternalStruct)
|
||||
}
|
||||
externalStructs[output.Type.Package][output.Type.Name] = &ExternalStruct{
|
||||
Package: output.Type.Package,
|
||||
Name: output.Type.Name,
|
||||
}
|
||||
jsType = output.NamespacedStructVariable(output.Type.Package)
|
||||
}
|
||||
returns += jsType + ", "
|
||||
}
|
||||
@@ -196,7 +216,7 @@ func GenerateBinding(structName string, method *BoundMethod, useIDs bool) (strin
|
||||
}
|
||||
result = strings.ReplaceAll(result, " * @returns {Promise<string>}", returns)
|
||||
|
||||
return result, lo.Uniq(models), lo.Uniq(namespacedStructs)
|
||||
return result, lo.Uniq(models), externalStructs
|
||||
}
|
||||
|
||||
func getPackageName(input *Parameter) string {
|
||||
@@ -240,47 +260,81 @@ func normalisePackageNames(packageNames []string) map[string]string {
|
||||
return result
|
||||
}
|
||||
|
||||
func GenerateBindings(bindings map[string]map[string][]*BoundMethod, useIDs bool) map[string]string {
|
||||
func (p *Project) GenerateBindings(bindings map[string]map[string][]*BoundMethod, useIDs bool) map[string]map[string]string {
|
||||
|
||||
var result = make(map[string]string)
|
||||
var result = make(map[string]map[string]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
|
||||
var allNamespacedStructs []string
|
||||
|
||||
packageBindings := bindings[packageName]
|
||||
structNames := lo.Keys(packageBindings)
|
||||
relativePackageDir := p.RelativePackageDir(packageName)
|
||||
_ = relativePackageDir
|
||||
sort.Strings(structNames)
|
||||
for _, structName := range structNames {
|
||||
result[normalisedPackageNames[packageName]] += "export const " + structName + " = {\n"
|
||||
if _, ok := result[relativePackageDir]; !ok {
|
||||
result[relativePackageDir] = make(map[string]string)
|
||||
}
|
||||
methods := packageBindings[structName]
|
||||
sort.Slice(methods, func(i, j int) bool {
|
||||
return methods[i].Name < methods[j].Name
|
||||
})
|
||||
var allNamespacedStructs map[packagePath]map[string]*ExternalStruct
|
||||
var namespacedStructs map[packagePath]map[string]*ExternalStruct
|
||||
var thisBinding string
|
||||
var models []string
|
||||
for _, method := range methods {
|
||||
thisBinding, models, namespacedStructs := GenerateBinding(structName, method, useIDs)
|
||||
allNamespacedStructs = append(allNamespacedStructs, namespacedStructs...)
|
||||
thisBinding, models, namespacedStructs = GenerateBinding(structName, method, useIDs)
|
||||
// Merge the namespaced structs
|
||||
allNamespacedStructs = mergeNamespacedStructs(allNamespacedStructs, namespacedStructs)
|
||||
allModels = append(allModels, models...)
|
||||
result[normalisedPackageNames[packageName]] += thisBinding
|
||||
result[relativePackageDir][structName] += thisBinding
|
||||
}
|
||||
result[normalisedPackageNames[packageName]] += "};\n\n"
|
||||
}
|
||||
|
||||
if len(allNamespacedStructs) > 0 {
|
||||
typedefs := "/**\n"
|
||||
for _, namespacedStruct := range lo.Uniq(allNamespacedStructs) {
|
||||
typedefs += " * @typedef {import('./models')." + namespacedStruct + "} " + strings.ReplaceAll(namespacedStruct, ".", "") + "\n"
|
||||
if len(allNamespacedStructs) > 0 {
|
||||
thisPkg := p.packageCache[packageName]
|
||||
typedefs := "/**\n"
|
||||
for externalPackageName, namespacedStruct := range allNamespacedStructs {
|
||||
pkgInfo := p.packageCache[externalPackageName]
|
||||
relativePackageDir := p.RelativeBindingsDir(thisPkg, pkgInfo)
|
||||
namePrefix := ""
|
||||
if pkgInfo.Name != "" && pkgInfo.Path != thisPkg.Path {
|
||||
namePrefix = pkgInfo.Name
|
||||
}
|
||||
|
||||
// Get keys from namespacedStruct and iterate over them in sorted order
|
||||
namespacedStructNames := lo.Keys(namespacedStruct)
|
||||
sort.Strings(namespacedStructNames)
|
||||
for _, thisStructName := range namespacedStructNames {
|
||||
structInfo := namespacedStruct[thisStructName]
|
||||
typedefs += " * @typedef {import('" + relativePackageDir + "/models')." + thisStructName + "} " + namePrefix + structInfo.Name + "\n"
|
||||
}
|
||||
}
|
||||
typedefs += " */\n"
|
||||
result[relativePackageDir][structName] = typedefs + result[relativePackageDir][structName]
|
||||
}
|
||||
typedefs += " */\n\n"
|
||||
result[normalisedPackageNames[packageName]] = typedefs + result[normalisedPackageNames[packageName]]
|
||||
result[relativePackageDir][structName] = header + result[relativePackageDir][structName]
|
||||
}
|
||||
|
||||
result[normalisedPackageNames[packageName]] = header + result[normalisedPackageNames[packageName]]
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func mergeNamespacedStructs(structs map[packagePath]map[string]*ExternalStruct, structs2 map[packagePath]map[string]*ExternalStruct) map[packagePath]map[string]*ExternalStruct {
|
||||
if structs == nil {
|
||||
structs = make(map[packagePath]map[string]*ExternalStruct)
|
||||
}
|
||||
for pkg, pkgStructs := range structs2 {
|
||||
if _, ok := structs[pkg]; !ok {
|
||||
structs[pkg] = make(map[string]*ExternalStruct)
|
||||
}
|
||||
for name, structInfo := range pkgStructs {
|
||||
structs[pkg][name] = structInfo
|
||||
}
|
||||
}
|
||||
return structs
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@ package parser
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
//go:embed testdata
|
||||
@@ -25,135 +25,342 @@ func TestGenerateBindings(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
dir string
|
||||
want map[string]string
|
||||
want map[string]map[string]string
|
||||
useIDs bool
|
||||
}{
|
||||
{
|
||||
"testdata/enum",
|
||||
map[string]string{
|
||||
"main": getFile("testdata/enum/bindings_main.js"),
|
||||
dir: "testdata/enum",
|
||||
want: map[string]map[string]string{
|
||||
"main": {
|
||||
"GreetService": getFile("testdata/enum/frontend/bindings/main/GreetService.js"),
|
||||
},
|
||||
},
|
||||
true,
|
||||
useIDs: true,
|
||||
},
|
||||
{
|
||||
"testdata/enum",
|
||||
map[string]string{
|
||||
"main": getFile("testdata/enum/bindings_main.name.js"),
|
||||
dir: "testdata/enum",
|
||||
want: map[string]map[string]string{
|
||||
"main": {
|
||||
"GreetService": getFile("testdata/enum/frontend/bindings/main/GreetService.name.js"),
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
// TODO: Fix this test to pull in enum
|
||||
{
|
||||
"testdata/enum_from_imported_package",
|
||||
map[string]string{
|
||||
"main": getFile("testdata/enum_from_imported_package/bindings_main.js"),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"testdata/function_single",
|
||||
map[string]string{
|
||||
"main": getFile("testdata/function_single/bindings_main.js"),
|
||||
dir: "testdata/enum_from_imported_package",
|
||||
want: map[string]map[string]string{
|
||||
"main": {
|
||||
"GreetService": getFile("testdata/enum_from_imported_package/frontend/bindings/main/GreetService.name.js"),
|
||||
},
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"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"),
|
||||
dir: "testdata/enum_from_imported_package",
|
||||
want: map[string]map[string]string{
|
||||
"main": {
|
||||
"GreetService": getFile("testdata/enum_from_imported_package/frontend/bindings/main/GreetService.js"),
|
||||
},
|
||||
},
|
||||
true,
|
||||
useIDs: true,
|
||||
},
|
||||
{
|
||||
"testdata/variable_single",
|
||||
map[string]string{
|
||||
"main": getFile("testdata/variable_single/bindings_main.js"),
|
||||
dir: "testdata/function_single",
|
||||
want: map[string]map[string]string{
|
||||
"main": {
|
||||
"GreetService": getFile("testdata/function_single/frontend/bindings/main/GreetService.js"),
|
||||
},
|
||||
},
|
||||
true,
|
||||
useIDs: true,
|
||||
},
|
||||
{
|
||||
"testdata/variable_single_from_function",
|
||||
map[string]string{
|
||||
"main": getFile("testdata/variable_single_from_function/bindings_main.js"),
|
||||
dir: "testdata/function_single",
|
||||
want: map[string]map[string]string{
|
||||
"main": {
|
||||
"GreetService": getFile("testdata/function_single/frontend/bindings/main/GreetService.name.js"),
|
||||
},
|
||||
},
|
||||
true,
|
||||
useIDs: false,
|
||||
},
|
||||
{
|
||||
"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"),
|
||||
dir: "testdata/function_from_imported_package",
|
||||
want: map[string]map[string]string{
|
||||
"main": {
|
||||
"GreetService": getFile("testdata/function_from_imported_package/frontend/bindings/main/GreetService.name.js"),
|
||||
},
|
||||
"services": {
|
||||
"OtherService": getFile("testdata/function_from_imported_package/frontend/bindings/services/OtherService.name.js"),
|
||||
},
|
||||
},
|
||||
true,
|
||||
useIDs: false,
|
||||
},
|
||||
{
|
||||
"testdata/struct_literal_single",
|
||||
map[string]string{
|
||||
"main": getFile("testdata/struct_literal_single/bindings_main.js"),
|
||||
dir: "testdata/function_from_imported_package",
|
||||
want: map[string]map[string]string{
|
||||
"main": {
|
||||
"GreetService": getFile("testdata/function_from_imported_package/frontend/bindings/main/GreetService.js"),
|
||||
},
|
||||
"services": {
|
||||
"OtherService": getFile("testdata/function_from_imported_package/frontend/bindings/services/OtherService.js"),
|
||||
},
|
||||
},
|
||||
true,
|
||||
useIDs: true,
|
||||
},
|
||||
{
|
||||
"testdata/struct_literal_multiple",
|
||||
map[string]string{
|
||||
"main": getFile("testdata/struct_literal_multiple/bindings_main.js"),
|
||||
dir: "testdata/function_from_nested_imported_package",
|
||||
want: map[string]map[string]string{
|
||||
"main": {
|
||||
"GreetService": getFile("testdata/function_from_nested_imported_package/frontend/bindings/main/GreetService.name.js"),
|
||||
},
|
||||
"services/other": {
|
||||
"OtherService": getFile("testdata/function_from_nested_imported_package/frontend/bindings/services/other/OtherService.name.js"),
|
||||
},
|
||||
},
|
||||
true,
|
||||
useIDs: false,
|
||||
},
|
||||
{
|
||||
"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"),
|
||||
dir: "testdata/function_from_nested_imported_package",
|
||||
want: map[string]map[string]string{
|
||||
"main": {
|
||||
"GreetService": getFile("testdata/function_from_nested_imported_package/frontend/bindings/main/GreetService.js"),
|
||||
},
|
||||
"services/other": {
|
||||
"OtherService": getFile("testdata/function_from_nested_imported_package/frontend/bindings/services/other/OtherService.js"),
|
||||
},
|
||||
},
|
||||
true,
|
||||
useIDs: true,
|
||||
},
|
||||
{
|
||||
"testdata/struct_literal_multiple_files",
|
||||
map[string]string{
|
||||
"main": getFile("testdata/struct_literal_multiple_files/bindings_main.js"),
|
||||
dir: "testdata/struct_literal_multiple",
|
||||
want: map[string]map[string]string{
|
||||
"main": {
|
||||
"GreetService": getFile("testdata/struct_literal_multiple/frontend/bindings/main/GreetService.js"),
|
||||
"OtherService": getFile("testdata/struct_literal_multiple/frontend/bindings/main/OtherService.js"),
|
||||
},
|
||||
},
|
||||
true,
|
||||
useIDs: true,
|
||||
},
|
||||
{
|
||||
dir: "testdata/struct_literal_multiple",
|
||||
want: map[string]map[string]string{
|
||||
"main": {
|
||||
"GreetService": getFile("testdata/struct_literal_multiple/frontend/bindings/main/GreetService.name.js"),
|
||||
"OtherService": getFile("testdata/struct_literal_multiple/frontend/bindings/main/OtherService.name.js"),
|
||||
},
|
||||
},
|
||||
useIDs: false,
|
||||
},
|
||||
{
|
||||
dir: "testdata/function_from_imported_package",
|
||||
want: map[string]map[string]string{
|
||||
"main": {
|
||||
"GreetService": getFile("testdata/function_from_imported_package/frontend/bindings/main/GreetService.js"),
|
||||
},
|
||||
"services": {
|
||||
"OtherService": getFile("testdata/function_from_imported_package/frontend/bindings/services/OtherService.js"),
|
||||
},
|
||||
},
|
||||
useIDs: true,
|
||||
},
|
||||
{
|
||||
dir: "testdata/function_from_imported_package",
|
||||
want: map[string]map[string]string{
|
||||
"main": {
|
||||
"GreetService": getFile("testdata/function_from_imported_package/frontend/bindings/main/GreetService.name.js"),
|
||||
},
|
||||
"services": {
|
||||
"OtherService": getFile("testdata/function_from_imported_package/frontend/bindings/services/OtherService.name.js"),
|
||||
},
|
||||
},
|
||||
useIDs: false,
|
||||
},
|
||||
{
|
||||
dir: "testdata/variable_single",
|
||||
want: map[string]map[string]string{
|
||||
"main": {
|
||||
"GreetService": getFile("testdata/variable_single/frontend/bindings/main/GreetService.name.js"),
|
||||
},
|
||||
},
|
||||
useIDs: false,
|
||||
},
|
||||
{
|
||||
dir: "testdata/variable_single",
|
||||
want: map[string]map[string]string{
|
||||
"main": {
|
||||
"GreetService": getFile("testdata/variable_single/frontend/bindings/main/GreetService.js"),
|
||||
},
|
||||
},
|
||||
useIDs: true,
|
||||
},
|
||||
{
|
||||
dir: "testdata/variable_single_from_function",
|
||||
want: map[string]map[string]string{
|
||||
"main": {
|
||||
"GreetService": getFile("testdata/variable_single_from_function/frontend/bindings/main/GreetService.name.js"),
|
||||
},
|
||||
},
|
||||
useIDs: false,
|
||||
},
|
||||
{
|
||||
dir: "testdata/variable_single_from_function",
|
||||
want: map[string]map[string]string{
|
||||
"main": {
|
||||
"GreetService": getFile("testdata/variable_single_from_function/frontend/bindings/main/GreetService.js"),
|
||||
},
|
||||
},
|
||||
useIDs: true,
|
||||
},
|
||||
{
|
||||
dir: "testdata/variable_single_from_other_function",
|
||||
want: map[string]map[string]string{
|
||||
"main": {
|
||||
"GreetService": getFile("testdata/variable_single_from_other_function/frontend/bindings/main/GreetService.name.js"),
|
||||
},
|
||||
"services": {
|
||||
"OtherService": getFile("testdata/variable_single_from_other_function/frontend/bindings/services/OtherService.name.js"),
|
||||
},
|
||||
},
|
||||
useIDs: false,
|
||||
},
|
||||
{
|
||||
dir: "testdata/variable_single_from_other_function",
|
||||
want: map[string]map[string]string{
|
||||
"main": {
|
||||
"GreetService": getFile("testdata/variable_single_from_other_function/frontend/bindings/main/GreetService.js"),
|
||||
},
|
||||
"services": {
|
||||
"OtherService": getFile("testdata/variable_single_from_other_function/frontend/bindings/services/OtherService.js"),
|
||||
},
|
||||
},
|
||||
useIDs: true,
|
||||
},
|
||||
{
|
||||
dir: "testdata/struct_literal_single",
|
||||
want: map[string]map[string]string{
|
||||
"main": {
|
||||
"GreetService": getFile("testdata/struct_literal_single/frontend/bindings/main/GreetService.name.js"),
|
||||
},
|
||||
},
|
||||
useIDs: false,
|
||||
},
|
||||
{
|
||||
dir: "testdata/struct_literal_single",
|
||||
want: map[string]map[string]string{
|
||||
"main": {
|
||||
"GreetService": getFile("testdata/struct_literal_single/frontend/bindings/main/GreetService.js"),
|
||||
},
|
||||
},
|
||||
useIDs: true,
|
||||
},
|
||||
{
|
||||
dir: "testdata/struct_literal_multiple_other",
|
||||
want: map[string]map[string]string{
|
||||
"main": {
|
||||
"GreetService": getFile("testdata/struct_literal_multiple_other/frontend/bindings/main/GreetService.name.js"),
|
||||
},
|
||||
"services": {
|
||||
"OtherService": getFile("testdata/struct_literal_multiple_other/frontend/bindings/services/OtherService.name.js"),
|
||||
},
|
||||
},
|
||||
useIDs: false,
|
||||
},
|
||||
{
|
||||
dir: "testdata/struct_literal_multiple_other",
|
||||
want: map[string]map[string]string{
|
||||
"main": {
|
||||
"GreetService": getFile("testdata/struct_literal_multiple_other/frontend/bindings/main/GreetService.js"),
|
||||
},
|
||||
"services": {
|
||||
"OtherService": getFile("testdata/struct_literal_multiple_other/frontend/bindings/services/OtherService.js"),
|
||||
},
|
||||
},
|
||||
useIDs: true,
|
||||
},
|
||||
{
|
||||
dir: "testdata/struct_literal_non_pointer_single",
|
||||
want: map[string]map[string]string{
|
||||
"main": {
|
||||
"GreetService": getFile("testdata/struct_literal_non_pointer_single/frontend/bindings/main/GreetService.name.js"),
|
||||
},
|
||||
},
|
||||
useIDs: false,
|
||||
},
|
||||
{
|
||||
dir: "testdata/struct_literal_non_pointer_single",
|
||||
want: map[string]map[string]string{
|
||||
"main": {
|
||||
"GreetService": getFile("testdata/struct_literal_non_pointer_single/frontend/bindings/main/GreetService.js"),
|
||||
},
|
||||
},
|
||||
useIDs: true,
|
||||
},
|
||||
{
|
||||
dir: "testdata/struct_literal_multiple_files",
|
||||
want: map[string]map[string]string{
|
||||
"main": {
|
||||
"GreetService": getFile("testdata/struct_literal_multiple_files/frontend/bindings/main/GreetService.name.js"),
|
||||
"OtherService": getFile("testdata/struct_literal_multiple_files/frontend/bindings/main/OtherService.name.js"),
|
||||
},
|
||||
},
|
||||
useIDs: false,
|
||||
},
|
||||
{
|
||||
dir: "testdata/struct_literal_multiple_files",
|
||||
want: map[string]map[string]string{
|
||||
"main": {
|
||||
"GreetService": getFile("testdata/struct_literal_multiple_files/frontend/bindings/main/GreetService.js"),
|
||||
"OtherService": getFile("testdata/struct_literal_multiple_files/frontend/bindings/main/OtherService.js"),
|
||||
},
|
||||
},
|
||||
useIDs: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.dir, func(t *testing.T) {
|
||||
// Run parser on directory
|
||||
project, err := ParseProject(tt.dir)
|
||||
absDir, err := filepath.Abs(tt.dir)
|
||||
if err != nil {
|
||||
t.Errorf("filepath.Abs() error = %v", err)
|
||||
return
|
||||
}
|
||||
project, err := ParseProject(absDir)
|
||||
if err != nil {
|
||||
t.Errorf("ParseProject() error = %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
project.outputDirectory = "frontend/bindings"
|
||||
|
||||
// Generate Bindings
|
||||
got := GenerateBindings(project.BoundMethods, tt.useIDs)
|
||||
got := project.GenerateBindings(project.BoundMethods, tt.useIDs)
|
||||
|
||||
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)
|
||||
for dirName, structDetails := range got {
|
||||
// iterate the struct names in structDetails
|
||||
for name, binding := range structDetails {
|
||||
expected, ok := tt.want[dirName][name]
|
||||
if !ok {
|
||||
outFile := filepath.Join(tt.dir, project.outputDirectory, dirName, name+".got.js")
|
||||
err = os.WriteFile(outFile, []byte(binding), 0644)
|
||||
if err != nil {
|
||||
t.Errorf("os.WriteFile() error = %v", err)
|
||||
return
|
||||
}
|
||||
t.Errorf("GenerateBindings() unexpected binding = %v", name)
|
||||
return
|
||||
}
|
||||
t.Errorf("GenerateBindings() unexpected binding = %v", name)
|
||||
return
|
||||
}
|
||||
// compare the binding
|
||||
// compare the binding
|
||||
|
||||
// convert all line endings to \n
|
||||
binding = convertLineEndings(binding)
|
||||
expected = convertLineEndings(expected)
|
||||
// convert all line endings to \n
|
||||
binding = convertLineEndings(binding)
|
||||
expected = convertLineEndings(expected)
|
||||
|
||||
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
|
||||
if diff := cmp.Diff(expected, binding); diff != "" {
|
||||
outFile := filepath.Join(tt.dir, project.outputDirectory, dirName, name+".got.js")
|
||||
err = os.WriteFile(outFile, []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("GenerateBindings() mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -17,9 +17,10 @@ type ModelDefinitions struct {
|
||||
Package string
|
||||
Models map[string]*StructDef
|
||||
Enums map[string]*TypeDef
|
||||
Imports []*ImportDef
|
||||
}
|
||||
|
||||
func GenerateModel(wr io.Writer, def *ModelDefinitions, options *flags.GenerateBindingsOptions) error {
|
||||
func (p *Project) GenerateModel(wr io.Writer, def *ModelDefinitions, options *flags.GenerateBindingsOptions) error {
|
||||
templateName := "model.js.tmpl"
|
||||
if options.TS {
|
||||
templateName = "model.ts.tmpl"
|
||||
@@ -57,13 +58,15 @@ func pkgAlias(fullPkg string) string {
|
||||
return pkgParts[len(pkgParts)-1]
|
||||
}
|
||||
|
||||
func GenerateModels(models map[packagePath]map[structName]*StructDef, enums map[packagePath]map[string]*TypeDef, options *flags.GenerateBindingsOptions) (string, error) {
|
||||
if models == nil && enums == nil {
|
||||
return "", nil
|
||||
}
|
||||
type Model struct {
|
||||
Package string
|
||||
}
|
||||
|
||||
var buffer bytes.Buffer
|
||||
buffer.WriteString(modelsHeader)
|
||||
func (p *Project) GenerateModels(models map[packagePath]map[structName]*StructDef, enums map[packagePath]map[string]*TypeDef, options *flags.GenerateBindingsOptions) (map[packagePath]string, error) {
|
||||
if models == nil && enums == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var result = make(map[packagePath]string)
|
||||
|
||||
// sort pkgs by alias (e.g. services) instead of full pkg name (e.g. github.com/wailsapp/wails/somedir/services)
|
||||
// and then sort resulting list by the alias
|
||||
@@ -80,14 +83,21 @@ func GenerateModels(models map[packagePath]map[structName]*StructDef, enums map[
|
||||
})
|
||||
|
||||
for _, pkg := range keys {
|
||||
err := GenerateModel(&buffer, &ModelDefinitions{
|
||||
var buffer bytes.Buffer
|
||||
buffer.WriteString(modelsHeader)
|
||||
err := p.GenerateModel(&buffer, &ModelDefinitions{
|
||||
Imports: p.calculateImports(pkg, models[pkg]),
|
||||
Package: pkgAlias(pkg),
|
||||
Models: models[pkg],
|
||||
Enums: enums[pkg],
|
||||
}, options)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get the relative package path
|
||||
relativePackageDir := p.RelativePackageDir(pkg)
|
||||
result[relativePackageDir] = buffer.String()
|
||||
}
|
||||
return buffer.String(), nil
|
||||
return result, nil
|
||||
}
|
||||
|
||||
+205
-104
@@ -14,109 +14,199 @@ func TestGenerateModels(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
dir string
|
||||
want string
|
||||
want map[string]string
|
||||
useInterface bool
|
||||
useTypescript bool
|
||||
}{
|
||||
// enum
|
||||
{
|
||||
name: "function single",
|
||||
dir: "testdata/function_single",
|
||||
useTypescript: true,
|
||||
},
|
||||
{
|
||||
name: "function from imported package",
|
||||
dir: "testdata/function_from_imported_package",
|
||||
want: getFile("testdata/function_from_imported_package/models.ts"),
|
||||
useTypescript: true,
|
||||
},
|
||||
{
|
||||
name: "function from imported package (Javascript)",
|
||||
dir: "testdata/function_from_imported_package",
|
||||
want: getFile("testdata/function_from_imported_package/models.js"),
|
||||
},
|
||||
{
|
||||
name: "variable single",
|
||||
dir: "testdata/variable_single",
|
||||
useTypescript: true,
|
||||
},
|
||||
{
|
||||
name: "variable single from function",
|
||||
dir: "testdata/variable_single_from_function",
|
||||
useTypescript: true,
|
||||
},
|
||||
{
|
||||
name: "variable single from other function",
|
||||
dir: "testdata/variable_single_from_other_function",
|
||||
want: getFile("testdata/variable_single_from_other_function/models.ts"),
|
||||
useTypescript: true,
|
||||
},
|
||||
{
|
||||
name: "struct literal single",
|
||||
dir: "testdata/struct_literal_single",
|
||||
want: getFile("testdata/struct_literal_single/models.ts"),
|
||||
useTypescript: true,
|
||||
},
|
||||
{
|
||||
name: "struct literal multiple",
|
||||
dir: "testdata/struct_literal_multiple",
|
||||
useTypescript: true,
|
||||
},
|
||||
{
|
||||
name: "struct literal multiple other",
|
||||
dir: "testdata/struct_literal_multiple_other",
|
||||
want: getFile("testdata/struct_literal_multiple_other/models.ts"),
|
||||
useTypescript: true,
|
||||
},
|
||||
{
|
||||
name: "struct literal multiple other (Javascript)",
|
||||
dir: "testdata/struct_literal_multiple_other",
|
||||
want: getFile("testdata/struct_literal_multiple_other/models.js"),
|
||||
},
|
||||
{
|
||||
name: "struct literal non pointer single (Javascript)",
|
||||
dir: "testdata/struct_literal_non_pointer_single",
|
||||
want: getFile("testdata/struct_literal_non_pointer_single/models.ts"),
|
||||
useTypescript: true,
|
||||
},
|
||||
{
|
||||
name: "struct literal non pointer single (Javascript)",
|
||||
dir: "testdata/struct_literal_non_pointer_single",
|
||||
want: getFile("testdata/struct_literal_non_pointer_single/models.js"),
|
||||
},
|
||||
{
|
||||
name: "struct literal multiple files",
|
||||
dir: "testdata/struct_literal_multiple_files",
|
||||
useTypescript: true,
|
||||
},
|
||||
{
|
||||
name: "enum",
|
||||
dir: "testdata/enum",
|
||||
want: getFile("testdata/enum/models.ts"),
|
||||
useTypescript: true,
|
||||
},
|
||||
{
|
||||
name: "enum (Javascript)",
|
||||
name: "enum - Typescript",
|
||||
dir: "testdata/enum",
|
||||
want: getFile("testdata/enum/models.js"),
|
||||
},
|
||||
{
|
||||
name: "enum from imported package",
|
||||
dir: "testdata/enum_from_imported_package",
|
||||
want: getFile("testdata/enum_from_imported_package/models.ts"),
|
||||
want: map[string]string{
|
||||
"main": getFile("testdata/enum/frontend/bindings/main/models.ts"),
|
||||
},
|
||||
useTypescript: true,
|
||||
},
|
||||
{
|
||||
name: "enum from imported package",
|
||||
dir: "testdata/enum_from_imported_package",
|
||||
want: getFile("testdata/enum_from_imported_package/models.js"),
|
||||
name: "enum - Javascript",
|
||||
dir: "testdata/enum",
|
||||
want: map[string]string{
|
||||
"main": getFile("testdata/enum/frontend/bindings/main/models.js"),
|
||||
},
|
||||
useTypescript: false,
|
||||
},
|
||||
{
|
||||
name: "enum interface",
|
||||
dir: "testdata/enum-interface",
|
||||
want: getFile("testdata/enum-interface/models.ts"),
|
||||
useInterface: true,
|
||||
name: "enum - Typescript interfaces",
|
||||
dir: "testdata/enum",
|
||||
want: map[string]string{
|
||||
"main": getFile("testdata/enum/frontend/bindings/main/models.interfaces.ts"),
|
||||
},
|
||||
useTypescript: true,
|
||||
useInterface: true,
|
||||
},
|
||||
// function from imported package
|
||||
{
|
||||
name: "function from imported package - Typescript",
|
||||
dir: "testdata/function_from_imported_package",
|
||||
want: map[string]string{
|
||||
"main": getFile("testdata/function_from_imported_package/frontend/bindings/main/models.ts"),
|
||||
"services": getFile("testdata/function_from_imported_package/frontend/bindings/services/models.ts"),
|
||||
},
|
||||
useTypescript: true,
|
||||
},
|
||||
{
|
||||
name: "function from imported package - Typescript interfaces",
|
||||
dir: "testdata/function_from_imported_package",
|
||||
want: map[string]string{
|
||||
"main": getFile("testdata/function_from_imported_package/frontend/bindings/main/models.interfaces.ts"),
|
||||
"services": getFile("testdata/function_from_imported_package/frontend/bindings/services/models.interfaces.ts"),
|
||||
},
|
||||
useTypescript: true,
|
||||
useInterface: true,
|
||||
},
|
||||
{
|
||||
name: "function from imported package - Javascript",
|
||||
dir: "testdata/function_from_imported_package",
|
||||
want: map[string]string{
|
||||
"main": getFile("testdata/function_from_imported_package/frontend/bindings/main/models.js"),
|
||||
"services": getFile("testdata/function_from_imported_package/frontend/bindings/services/models.js"),
|
||||
},
|
||||
useTypescript: false,
|
||||
},
|
||||
// variable single from other function
|
||||
{
|
||||
name: "variable single from other function - Typescript",
|
||||
dir: "testdata/variable_single_from_other_function",
|
||||
want: map[string]string{
|
||||
"main": getFile("testdata/variable_single_from_other_function/frontend/bindings/main/models.ts"),
|
||||
"services": getFile("testdata/variable_single_from_other_function/frontend/bindings/services/models.ts"),
|
||||
},
|
||||
useTypescript: true,
|
||||
},
|
||||
{
|
||||
name: "variable single from other function - Typescript interfaces",
|
||||
dir: "testdata/variable_single_from_other_function",
|
||||
want: map[string]string{
|
||||
"main": getFile("testdata/variable_single_from_other_function/frontend/bindings/main/models.interfaces.ts"),
|
||||
"services": getFile("testdata/variable_single_from_other_function/frontend/bindings/services/models.interfaces.ts"),
|
||||
},
|
||||
useTypescript: true,
|
||||
useInterface: true,
|
||||
},
|
||||
{
|
||||
name: "variable single from other function - Javascript",
|
||||
dir: "testdata/variable_single_from_other_function",
|
||||
want: map[string]string{
|
||||
"main": getFile("testdata/variable_single_from_other_function/frontend/bindings/main/models.js"),
|
||||
"services": getFile("testdata/variable_single_from_other_function/frontend/bindings/services/models.js"),
|
||||
},
|
||||
useTypescript: false,
|
||||
},
|
||||
// struct literal single
|
||||
{
|
||||
name: "struct literal single - Typescript",
|
||||
dir: "testdata/struct_literal_single",
|
||||
want: map[string]string{
|
||||
"main": getFile("testdata/struct_literal_single/frontend/bindings/main/models.ts"),
|
||||
},
|
||||
useTypescript: true,
|
||||
},
|
||||
{
|
||||
name: "struct literal single - Typescript interfaces",
|
||||
dir: "testdata/struct_literal_single",
|
||||
want: map[string]string{
|
||||
"main": getFile("testdata/struct_literal_single/frontend/bindings/main/models.interfaces.ts"),
|
||||
},
|
||||
useTypescript: true,
|
||||
useInterface: true,
|
||||
},
|
||||
{
|
||||
name: "struct literal single - Javascript",
|
||||
dir: "testdata/struct_literal_single",
|
||||
want: map[string]string{
|
||||
"main": getFile("testdata/struct_literal_single/frontend/bindings/main/models.js"),
|
||||
},
|
||||
useTypescript: false,
|
||||
},
|
||||
// struct literal multiple other
|
||||
{
|
||||
name: "struct literal multiple other - Typescript",
|
||||
dir: "testdata/struct_literal_multiple_other",
|
||||
want: map[string]string{
|
||||
"main": getFile("testdata/struct_literal_multiple_other/frontend/bindings/main/models.ts"),
|
||||
"services": getFile("testdata/struct_literal_multiple_other/frontend/bindings/services/models.ts"),
|
||||
},
|
||||
useTypescript: true,
|
||||
},
|
||||
{
|
||||
name: "struct literal multiple other - Typescript interfaces",
|
||||
dir: "testdata/struct_literal_multiple_other",
|
||||
want: map[string]string{
|
||||
"main": getFile("testdata/struct_literal_multiple_other/frontend/bindings/main/models.interfaces.ts"),
|
||||
"services": getFile("testdata/struct_literal_multiple_other/frontend/bindings/services/models.interfaces.ts"),
|
||||
},
|
||||
useTypescript: true,
|
||||
useInterface: true,
|
||||
},
|
||||
{
|
||||
name: "struct literal multiple other - Javascript",
|
||||
dir: "testdata/struct_literal_multiple_other",
|
||||
want: map[string]string{
|
||||
"main": getFile("testdata/struct_literal_multiple_other/frontend/bindings/main/models.js"),
|
||||
"services": getFile("testdata/struct_literal_multiple_other/frontend/bindings/services/models.js"),
|
||||
},
|
||||
useTypescript: false,
|
||||
},
|
||||
// struct literal non pointer single
|
||||
{
|
||||
name: "struct literal non pointer single - Typescript",
|
||||
dir: "testdata/struct_literal_non_pointer_single",
|
||||
want: map[string]string{
|
||||
"main": getFile("testdata/struct_literal_non_pointer_single/frontend/bindings/main/models.ts"),
|
||||
},
|
||||
useTypescript: true,
|
||||
},
|
||||
{
|
||||
name: "struct literal non pointer single - Typescript interfaces",
|
||||
dir: "testdata/struct_literal_non_pointer_single",
|
||||
want: map[string]string{
|
||||
"main": getFile("testdata/struct_literal_non_pointer_single/frontend/bindings/main/models.interfaces.ts"),
|
||||
},
|
||||
useTypescript: true,
|
||||
useInterface: true,
|
||||
},
|
||||
{
|
||||
name: "struct literal non pointer single - Javascript",
|
||||
dir: "testdata/struct_literal_non_pointer_single",
|
||||
want: map[string]string{
|
||||
"main": getFile("testdata/struct_literal_non_pointer_single/frontend/bindings/main/models.js"),
|
||||
},
|
||||
useTypescript: false,
|
||||
},
|
||||
// enum from imported package
|
||||
{
|
||||
name: "enum from imported package - Typescript",
|
||||
dir: "testdata/enum_from_imported_package",
|
||||
want: map[string]string{
|
||||
"services": getFile("testdata/enum_from_imported_package/frontend/bindings/services/models.ts"),
|
||||
},
|
||||
useTypescript: true,
|
||||
},
|
||||
{
|
||||
name: "enum from imported package - Typescript interfaces",
|
||||
dir: "testdata/enum_from_imported_package",
|
||||
want: map[string]string{
|
||||
"services": getFile("testdata/enum_from_imported_package/frontend/bindings/services/models.interfaces.ts"),
|
||||
},
|
||||
useTypescript: true,
|
||||
},
|
||||
{
|
||||
name: "enum from imported package - Javascript",
|
||||
dir: "testdata/enum_from_imported_package",
|
||||
want: map[string]string{
|
||||
"services": getFile("testdata/enum_from_imported_package/frontend/bindings/services/models.js"),
|
||||
},
|
||||
useTypescript: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
@@ -127,28 +217,39 @@ func TestGenerateModels(t *testing.T) {
|
||||
t.Fatalf("ParseProject() error = %v", err)
|
||||
}
|
||||
|
||||
project.outputDirectory = "frontend/bindings"
|
||||
|
||||
// Generate Models
|
||||
got, err := GenerateModels(project.Models, project.Types, &flags.GenerateBindingsOptions{
|
||||
allModels, err := project.GenerateModels(project.Models, project.Types, &flags.GenerateBindingsOptions{
|
||||
UseInterfaces: tt.useInterface,
|
||||
TS: tt.useTypescript,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateModels() error = %v", err)
|
||||
}
|
||||
// convert all line endings to \n
|
||||
got = convertLineEndings(got)
|
||||
want := convertLineEndings(tt.want)
|
||||
if diff := cmp.Diff(want, got); diff != "" {
|
||||
gotFilename := "models.got.js"
|
||||
if tt.useTypescript {
|
||||
gotFilename = "models.got.ts"
|
||||
for pkgDir, got := range allModels {
|
||||
// convert all line endings to \n
|
||||
got = convertLineEndings(got)
|
||||
want, ok := tt.want[pkgDir]
|
||||
if !ok {
|
||||
t.Fatalf("GenerateModels() missing package: %s", pkgDir)
|
||||
}
|
||||
err = os.WriteFile(filepath.Join(tt.dir, gotFilename), []byte(got), 0644)
|
||||
if err != nil {
|
||||
t.Errorf("os.WriteFile() error = %v", err)
|
||||
return
|
||||
want = convertLineEndings(want)
|
||||
if diff := cmp.Diff(want, got); diff != "" {
|
||||
gotFilename := "models.got.js"
|
||||
if tt.useTypescript {
|
||||
gotFilename = "models.got.ts"
|
||||
}
|
||||
// Get relative package path
|
||||
//relativeBindingsDir := project.RelativeBindingsDir(project.packageCache[pkgDir])
|
||||
|
||||
err = os.WriteFile(filepath.Join(tt.dir, project.outputDirectory, pkgDir, gotFilename), []byte(got), 0644)
|
||||
if err != nil {
|
||||
t.Errorf("os.WriteFile() error = %v", err)
|
||||
return
|
||||
}
|
||||
t.Fatalf("GenerateModels() mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
t.Fatalf("GenerateModels() mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
+125
-32
@@ -23,9 +23,9 @@ type packagePath = string
|
||||
type structName = string
|
||||
|
||||
type StructDef struct {
|
||||
Name string
|
||||
DocComment string
|
||||
Fields []*Field
|
||||
Name string
|
||||
DocComments []string
|
||||
Fields []*Field
|
||||
}
|
||||
|
||||
func (s *StructDef) DefaultValueList() string {
|
||||
@@ -66,24 +66,24 @@ type Parameter struct {
|
||||
Type *ParameterType
|
||||
}
|
||||
|
||||
func (p *Parameter) NamespacedStructType() string {
|
||||
func (p *Parameter) NamespacedStructType(pkgName string) string {
|
||||
var typeName string
|
||||
if p.Type.Package != "" {
|
||||
if p.Type.Package != "" && p.Type.Package != pkgName {
|
||||
parts := strings.Split(p.Type.Package, "/")
|
||||
typeName = parts[len(parts)-1] + "."
|
||||
}
|
||||
return typeName + p.Type.Name
|
||||
}
|
||||
func (p *Parameter) NamespacedStructVariable() string {
|
||||
func (p *Parameter) NamespacedStructVariable(pkgName string) string {
|
||||
var typeName string
|
||||
if p.Type.Package != "" {
|
||||
if p.Type.Package != "" && p.Type.Package != pkgName {
|
||||
parts := strings.Split(p.Type.Package, "/")
|
||||
typeName = parts[len(parts)-1]
|
||||
}
|
||||
return typeName + p.Type.Name
|
||||
}
|
||||
|
||||
func (p *Parameter) JSType() string {
|
||||
func (p *Parameter) JSType(pkgName string) string {
|
||||
// Convert type to javascript equivalent type
|
||||
var typeName string
|
||||
switch p.Type.Name {
|
||||
@@ -99,7 +99,7 @@ func (p *Parameter) JSType() string {
|
||||
|
||||
// if the type is a struct, we need to add the package name
|
||||
if p.Type.IsStruct || p.Type.IsEnum {
|
||||
typeName = p.NamespacedStructType()
|
||||
typeName = p.NamespacedStructType(pkgName)
|
||||
typeName = strings.ReplaceAll(typeName, ".", "")
|
||||
}
|
||||
|
||||
@@ -118,6 +118,7 @@ func (p *Parameter) JSType() string {
|
||||
|
||||
type BoundMethod struct {
|
||||
Package string
|
||||
PackageDir string
|
||||
Name string
|
||||
DocComment string
|
||||
Inputs []*Parameter
|
||||
@@ -131,8 +132,9 @@ func (m BoundMethod) IDAsString() string {
|
||||
}
|
||||
|
||||
type Field struct {
|
||||
Name string
|
||||
Type *ParameterType
|
||||
Name string
|
||||
Type *ParameterType
|
||||
Project *Project
|
||||
}
|
||||
|
||||
func (f *Field) JSName() string {
|
||||
@@ -195,17 +197,24 @@ func (f *Field) JSDocType(pkg string) string {
|
||||
jsType = f.Type.Name
|
||||
}
|
||||
|
||||
// If we are the same package, just return the type
|
||||
externalPkgInfo := f.Project.packageCache[f.Type.Package]
|
||||
if externalPkgInfo.Name == pkg {
|
||||
return jsType
|
||||
}
|
||||
|
||||
var result string
|
||||
isExternalStruct := f.Type.Package != "" && f.Type.Package != pkg && f.Type.IsStruct
|
||||
if f.Type.Package == "" || f.Type.Package == pkg || !isExternalStruct {
|
||||
if f.Type.Package == "" || !isExternalStruct {
|
||||
if f.Type.IsStruct || f.Type.IsEnum {
|
||||
result = fmt.Sprintf("%s.%s", pkg, jsType)
|
||||
// get the relative package directory
|
||||
result = fmt.Sprintf("%s%s", externalPkgInfo.Name, f.Type.Name)
|
||||
} else {
|
||||
result = jsType
|
||||
}
|
||||
} else {
|
||||
parts := strings.Split(f.Type.Package, "/")
|
||||
result += fmt.Sprintf("%s.%s", parts[len(parts)-1], jsType)
|
||||
// get the relative package directory
|
||||
result = fmt.Sprintf("%s%s", externalPkgInfo.Name, f.Type.Name)
|
||||
}
|
||||
|
||||
if !ast.IsExported(f.Name) {
|
||||
@@ -230,9 +239,9 @@ func (f *Field) DefaultValue() string {
|
||||
}
|
||||
|
||||
type ConstDef struct {
|
||||
Name string
|
||||
DocComment string
|
||||
Value string
|
||||
Name string
|
||||
DocComments []string
|
||||
Value string
|
||||
}
|
||||
|
||||
type TypeDef struct {
|
||||
@@ -258,6 +267,7 @@ type ParsedPackage struct {
|
||||
|
||||
type Project struct {
|
||||
packageCache map[string]*ParsedPackage
|
||||
outputDirectory string
|
||||
Path string
|
||||
BoundMethods map[packagePath]map[structName][]*BoundMethod
|
||||
Models map[packagePath]map[structName]*StructDef
|
||||
@@ -320,13 +330,21 @@ func GenerateBindingsAndModels(options *flags.GenerateBindingsOptions) error {
|
||||
return err
|
||||
}
|
||||
p.Stats.NumMethods = len(p.BoundMethods)
|
||||
generatedMethods := GenerateBindings(p.BoundMethods, options.UseIDs)
|
||||
for pkg, text := range generatedMethods {
|
||||
// Write the file
|
||||
err = os.WriteFile(filepath.Join(options.OutputDirectory, pkg+".js"), []byte(text), 0644)
|
||||
if err != nil {
|
||||
p.outputDirectory = options.OutputDirectory
|
||||
generatedMethods := p.GenerateBindings(p.BoundMethods, options.UseIDs)
|
||||
for pkg, structs := range generatedMethods {
|
||||
// Write the directory
|
||||
err = os.MkdirAll(filepath.Join(options.OutputDirectory, pkg), 0755)
|
||||
if err != nil && !os.IsExist(err) {
|
||||
return err
|
||||
}
|
||||
// Write the files
|
||||
for structName, text := range structs {
|
||||
err = os.WriteFile(filepath.Join(options.OutputDirectory, pkg, structName+".js"), []byte(text), 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p.Stats.NumModels = len(p.Models)
|
||||
@@ -334,11 +352,17 @@ func GenerateBindingsAndModels(options *flags.GenerateBindingsOptions) error {
|
||||
|
||||
// Generate Models
|
||||
if len(p.Models) > 0 {
|
||||
generatedModels, err := GenerateModels(p.Models, p.Types, options)
|
||||
generatedModels, err := p.GenerateModels(p.Models, p.Types, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.WriteFile(filepath.Join(options.OutputDirectory, options.ModelsFilename), []byte(generatedModels), 0644)
|
||||
for pkg, text := range generatedModels {
|
||||
// Get directory for package
|
||||
pkgInfo := p.packageCache[pkg]
|
||||
relativePackageDir := p.RelativeBindingsDir(pkgInfo, pkgInfo)
|
||||
// Write the directory
|
||||
err = os.WriteFile(filepath.Join(options.OutputDirectory, relativePackageDir, options.ModelsFilename), []byte(text), 0644)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -602,6 +626,7 @@ func (p *Project) parseBoundStructMethods(name string, pkg *ParsedPackage) error
|
||||
|
||||
method := &BoundMethod{
|
||||
Package: pkg.Path,
|
||||
PackageDir: pkg.Dir,
|
||||
ID: id,
|
||||
Name: funcDecl.Name.Name,
|
||||
DocComment: strings.TrimSpace(funcDecl.Doc.Text()),
|
||||
@@ -689,7 +714,8 @@ func (p *Project) parseParameterType(field *ast.Field, pkg *ParsedPackage) *Para
|
||||
result.Name = p.anonymousStructID()
|
||||
// Create a new struct definition
|
||||
result := &StructDef{
|
||||
Name: result.Name,
|
||||
Name: result.Name,
|
||||
DocComments: CommentGroupToText(field.Doc),
|
||||
}
|
||||
pkg.StructCache[result.Name] = result
|
||||
// Parse the fields
|
||||
@@ -750,8 +776,8 @@ func (p *Project) getStructDef(name string, pkg *ParsedPackage) bool {
|
||||
if structType, ok := typeSpec.Type.(*ast.StructType); ok {
|
||||
if typeSpec.Name.Name == name {
|
||||
result := &StructDef{
|
||||
Name: name,
|
||||
//TODO DocComment: CommentGroupToText(typeDecl.Doc),
|
||||
Name: name,
|
||||
DocComments: CommentGroupToText(typeDecl.Doc),
|
||||
}
|
||||
pkg.StructCache[name] = result
|
||||
result.Fields = p.parseStructFields(structType, pkg)
|
||||
@@ -774,12 +800,14 @@ func (p *Project) parseStructFields(structType *ast.StructType, pkg *ParsedPacka
|
||||
if len(field.Names) > 0 {
|
||||
for _, name := range field.Names {
|
||||
theseFields = append(theseFields, &Field{
|
||||
Name: name.Name,
|
||||
Project: p,
|
||||
Name: name.Name,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
theseFields = append(theseFields, &Field{
|
||||
Name: "",
|
||||
Project: p,
|
||||
Name: "",
|
||||
})
|
||||
}
|
||||
// loop over fields
|
||||
@@ -1079,7 +1107,7 @@ func (p *Project) parseConstDeclaration(decl *ast.GenDecl, pkg *ParsedPackage) {
|
||||
}
|
||||
|
||||
if valueSpec.Doc != nil {
|
||||
constDecl.DocComment = strings.TrimSpace(valueSpec.Doc.Text())
|
||||
constDecl.DocComments = CommentGroupToText(valueSpec.Doc)
|
||||
}
|
||||
typeDecl.Consts = append(typeDecl.Consts, constDecl)
|
||||
}
|
||||
@@ -1087,7 +1115,22 @@ func (p *Project) parseConstDeclaration(decl *ast.GenDecl, pkg *ParsedPackage) {
|
||||
}
|
||||
|
||||
func (p *Project) RelativePackageDir(path string) string {
|
||||
return strings.TrimPrefix(path, p.Path)
|
||||
|
||||
// Get the package details
|
||||
pkgInfo, ok := p.packageCache[path]
|
||||
if !ok {
|
||||
panic("package not found: " + path)
|
||||
}
|
||||
|
||||
result := filepath.ToSlash(strings.TrimPrefix(pkgInfo.Dir, p.Path))
|
||||
if result == "" {
|
||||
return "main"
|
||||
}
|
||||
// Remove the leading slash
|
||||
if result[0] == '/' || result[0] == '\\' {
|
||||
result = result[1:]
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (p *Project) parseTypes(pkgs map[string]*ParsedPackage) {
|
||||
@@ -1126,6 +1169,56 @@ func (p *Project) parseTypes(pkgs map[string]*ParsedPackage) {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Project) RelativeBindingsDir(dir *ParsedPackage, dir2 *ParsedPackage) string {
|
||||
|
||||
if dir.Dir == dir2.Dir {
|
||||
return "."
|
||||
}
|
||||
|
||||
// Calculate the relative path from the bindings directory to the package directory
|
||||
absoluteSourceDir := dir.Dir
|
||||
if absoluteSourceDir == p.Path {
|
||||
absoluteSourceDir = filepath.Join(p.Path, p.outputDirectory, "main")
|
||||
} else {
|
||||
absoluteSourceDir = dir.Dir
|
||||
}
|
||||
targetRelativeDir := strings.TrimPrefix(dir2.Dir, p.Path)
|
||||
targetBindingsDir := filepath.Join(p.Path, p.outputDirectory, targetRelativeDir)
|
||||
// Calculate the relative path from the source directory to the target directory
|
||||
relativePath, err := filepath.Rel(absoluteSourceDir, targetBindingsDir)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return filepath.ToSlash(relativePath)
|
||||
}
|
||||
|
||||
type ImportDef struct {
|
||||
Name string
|
||||
Path string
|
||||
VarName string
|
||||
PackageName string
|
||||
}
|
||||
|
||||
func (p *Project) calculateImports(pkg string, m map[structName]*StructDef) []*ImportDef {
|
||||
var result []*ImportDef
|
||||
for _, structDef := range m {
|
||||
for _, field := range structDef.Fields {
|
||||
if field.Type.Package != pkg {
|
||||
// Find the relative path from the source directory to the target directory
|
||||
fieldPkgInfo := p.packageCache[field.Type.Package]
|
||||
relativePath := p.RelativeBindingsDir(p.packageCache[pkg], fieldPkgInfo)
|
||||
result = append(result, &ImportDef{
|
||||
PackageName: fieldPkgInfo.Name,
|
||||
Name: field.Name,
|
||||
Path: relativePath,
|
||||
VarName: fieldPkgInfo.Name + field.Name,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func getTypeString(expr ast.Expr) string {
|
||||
switch t := expr.(type) {
|
||||
case *ast.Ident:
|
||||
|
||||
@@ -87,13 +87,14 @@ func TestParseEnum(t *testing.T) {
|
||||
wantTypes: map[string]map[string]*TypeDef{
|
||||
"main": {
|
||||
"Title": {
|
||||
Name: "Title",
|
||||
Type: "string",
|
||||
Name: "Title",
|
||||
DocComments: []string{"// Title is a title"},
|
||||
Type: "string",
|
||||
Consts: []*ConstDef{
|
||||
{
|
||||
Name: "Mister",
|
||||
DocComment: "Mister is a title",
|
||||
Value: `"Mr"`,
|
||||
Name: "Mister",
|
||||
DocComments: []string{"// Mister is a title"},
|
||||
Value: `"Mr"`,
|
||||
},
|
||||
{
|
||||
Name: "Miss",
|
||||
@@ -119,131 +120,8 @@ func TestParseEnum(t *testing.T) {
|
||||
wantModels: map[string]map[string]*StructDef{
|
||||
"main": {
|
||||
"Person": {
|
||||
Name: "Person",
|
||||
Fields: []*Field{
|
||||
{
|
||||
Name: "Title",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "Title",
|
||||
IsEnum: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Name",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "should find a bound service with an enum interface",
|
||||
dir: "testdata/enum-interface",
|
||||
wantErr: false,
|
||||
wantBoundMethods: map[string]map[string][]*BoundMethod{
|
||||
"main": {
|
||||
"GreetService": {
|
||||
{
|
||||
Package: "main",
|
||||
Name: "Greet",
|
||||
DocComment: "Greet does XYZ",
|
||||
Inputs: []*Parameter{
|
||||
{
|
||||
Name: "name",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "title",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "Title",
|
||||
IsEnum: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Name: "",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
ID: 1411160069,
|
||||
},
|
||||
{
|
||||
Package: "main",
|
||||
Name: "NewPerson",
|
||||
DocComment: "NewPerson creates a new person",
|
||||
Inputs: []*Parameter{
|
||||
{
|
||||
Name: "name",
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Type: &ParameterType{
|
||||
Package: "main",
|
||||
Name: "Person",
|
||||
IsStruct: true,
|
||||
IsPointer: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
ID: 1661412647,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantTypes: map[string]map[string]*TypeDef{
|
||||
"main": {
|
||||
"Title": {
|
||||
Name: "Title",
|
||||
Type: "string",
|
||||
Consts: []*ConstDef{
|
||||
{
|
||||
Name: "Mister",
|
||||
DocComment: "Mister is a title",
|
||||
Value: `"Mr"`,
|
||||
},
|
||||
{
|
||||
Name: "Miss",
|
||||
Value: `"Miss"`,
|
||||
},
|
||||
{
|
||||
Name: "Ms",
|
||||
Value: `"Ms"`,
|
||||
},
|
||||
{
|
||||
Name: "Mrs",
|
||||
Value: `"Mrs"`,
|
||||
},
|
||||
{
|
||||
Name: "Dr",
|
||||
Value: `"Dr"`,
|
||||
},
|
||||
},
|
||||
ShouldGenerate: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantModels: map[string]map[string]*StructDef{
|
||||
"main": {
|
||||
"Person": {
|
||||
Name: "Person",
|
||||
Name: "Person",
|
||||
DocComments: []string{"// Person represents a person"},
|
||||
Fields: []*Field{
|
||||
{
|
||||
Name: "Title",
|
||||
@@ -273,6 +151,36 @@ func TestParseEnum(t *testing.T) {
|
||||
t.Errorf("ParseDirectory() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
|
||||
// Patch the PackageDir in the wantBoundMethods
|
||||
for _, packageData := range got.BoundMethods {
|
||||
for _, boundMethods := range packageData {
|
||||
for _, boundMethod := range boundMethods {
|
||||
boundMethod.PackageDir = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loop over the things we want
|
||||
for packageName, packageData := range tt.wantBoundMethods {
|
||||
for structName, wantBoundMethods := range packageData {
|
||||
gotBoundMethods := got.BoundMethods[packageName][structName]
|
||||
if diff := cmp.Diff(wantBoundMethods, gotBoundMethods); diff != "" {
|
||||
t.Errorf("ParseDirectory() failed:\n" + diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loop over the models
|
||||
for _, packageData := range got.Models {
|
||||
for _, wantModel := range packageData {
|
||||
// Loop over the Fields
|
||||
for _, field := range wantModel.Fields {
|
||||
field.Project = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.wantBoundMethods, got.BoundMethods); diff != "" {
|
||||
t.Errorf("ParseDirectory() failed:\n" + diff)
|
||||
}
|
||||
|
||||
@@ -111,22 +111,23 @@ func TestParseFunction(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
"github.com/wailsapp/wails/v3/internal/parser/testdata/variable_single_from_other_function/services": {
|
||||
"github.com/wailsapp/wails/v3/internal/parser/testdata/function_from_imported_package/services": {
|
||||
"OtherService": {
|
||||
{
|
||||
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/variable_single_from_other_function/services",
|
||||
Name: "Yay",
|
||||
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/function_from_imported_package/services",
|
||||
Name: "Yay",
|
||||
DocComment: "Yay does this and that",
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Type: &ParameterType{
|
||||
Name: "Address",
|
||||
IsStruct: true,
|
||||
IsPointer: true,
|
||||
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/variable_single_from_other_function/services",
|
||||
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/function_from_imported_package/services",
|
||||
},
|
||||
},
|
||||
},
|
||||
ID: 302702907,
|
||||
ID: 1592414782,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -134,7 +135,8 @@ func TestParseFunction(t *testing.T) {
|
||||
wantModels: map[string]map[string]*StructDef{
|
||||
"main": {
|
||||
"Person": {
|
||||
Name: "Person",
|
||||
Name: "Person",
|
||||
DocComments: []string{"// Person is a person"},
|
||||
Fields: []*Field{
|
||||
{
|
||||
Name: "Name",
|
||||
@@ -149,34 +151,34 @@ func TestParseFunction(t *testing.T) {
|
||||
Name: "Address",
|
||||
IsStruct: true,
|
||||
IsPointer: true,
|
||||
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/variable_single_from_other_function/services",
|
||||
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/function_from_imported_package/services",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"github.com/wailsapp/wails/v3/internal/parser/testdata/variable_single_from_other_function/services": {
|
||||
"github.com/wailsapp/wails/v3/internal/parser/testdata/function_from_imported_package/services": {
|
||||
"Address": {
|
||||
Name: "Address",
|
||||
Fields: []*Field{
|
||||
{
|
||||
Name: "Street",
|
||||
Type: &ParameterType{
|
||||
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/variable_single_from_other_function/services",
|
||||
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/function_from_imported_package/services",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "State",
|
||||
Type: &ParameterType{
|
||||
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/variable_single_from_other_function/services",
|
||||
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/function_from_imported_package/services",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Country",
|
||||
Type: &ParameterType{
|
||||
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/variable_single_from_other_function/services",
|
||||
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/function_from_imported_package/services",
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
@@ -193,8 +195,40 @@ func TestParseFunction(t *testing.T) {
|
||||
t.Errorf("ParseDirectory() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if diff := cmp.Diff(tt.wantBoundMethods, got.BoundMethods); diff != "" {
|
||||
t.Errorf("ParseDirectory() failed:\n" + diff)
|
||||
|
||||
// Patch the PackageDir in the wantBoundMethods
|
||||
for _, packageData := range got.BoundMethods {
|
||||
for _, boundMethods := range packageData {
|
||||
for _, boundMethod := range boundMethods {
|
||||
boundMethod.PackageDir = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loop over the things we want
|
||||
for packageName, packageData := range tt.wantBoundMethods {
|
||||
for structName, wantBoundMethods := range packageData {
|
||||
gotBoundMethods := got.BoundMethods[packageName][structName]
|
||||
if diff := cmp.Diff(wantBoundMethods, gotBoundMethods); diff != "" {
|
||||
t.Errorf("ParseDirectory() failed:\n" + diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loop over the models
|
||||
for _, packageData := range got.Models {
|
||||
for _, wantModel := range packageData {
|
||||
// Loop over the Fields
|
||||
for _, field := range wantModel.Fields {
|
||||
field.Project = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(tt.wantBoundMethods, got.BoundMethods) {
|
||||
t.Errorf("ParseDirectory() failed:\n" + cmp.Diff(tt.wantBoundMethods, got.BoundMethods))
|
||||
//spew.Dump(tt.wantBoundMethods)
|
||||
//spew.Dump(got.BoundMethods)
|
||||
}
|
||||
if !reflect.DeepEqual(tt.wantModels, got.Models) {
|
||||
t.Errorf("ParseDirectory() failed:\n" + cmp.Diff(tt.wantModels, got.Models))
|
||||
|
||||
@@ -163,8 +163,9 @@ func TestParseStructLiteralMultiple(t *testing.T) {
|
||||
"github.com/wailsapp/wails/v3/internal/parser/testdata/struct_literal_multiple_other/services": {
|
||||
"OtherService": {
|
||||
{
|
||||
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/struct_literal_multiple_other/services",
|
||||
Name: "Yay",
|
||||
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/struct_literal_multiple_other/services",
|
||||
Name: "Yay",
|
||||
DocComment: "Yay does this and that",
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Type: &ParameterType{
|
||||
@@ -242,6 +243,36 @@ func TestParseStructLiteralMultiple(t *testing.T) {
|
||||
t.Errorf("ParseDirectory() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
|
||||
// Patch the PackageDir in the wantBoundMethods
|
||||
for _, packageData := range got.BoundMethods {
|
||||
for _, boundMethods := range packageData {
|
||||
for _, boundMethod := range boundMethods {
|
||||
boundMethod.PackageDir = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loop over the things we want
|
||||
for packageName, packageData := range tt.wantBoundMethods {
|
||||
for structName, wantBoundMethods := range packageData {
|
||||
gotBoundMethods := got.BoundMethods[packageName][structName]
|
||||
if diff := cmp.Diff(wantBoundMethods, gotBoundMethods); diff != "" {
|
||||
t.Errorf("ParseDirectory() failed:\n" + diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loop over the models
|
||||
for _, packageData := range got.Models {
|
||||
for _, wantModel := range packageData {
|
||||
// Loop over the Fields
|
||||
for _, field := range wantModel.Fields {
|
||||
field.Project = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.wantBoundMethods, got.BoundMethods); diff != "" {
|
||||
t.Errorf("ParseDirectory() failed:\n" + diff)
|
||||
}
|
||||
|
||||
@@ -1119,6 +1119,36 @@ func TestParseStructLiteralNonPointerSingle(t *testing.T) {
|
||||
t.Errorf("ParseDirectory() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
|
||||
// Patch the PackageDir in the wantBoundMethods
|
||||
for _, packageData := range got.BoundMethods {
|
||||
for _, boundMethods := range packageData {
|
||||
for _, boundMethod := range boundMethods {
|
||||
boundMethod.PackageDir = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loop over the things we want
|
||||
for packageName, packageData := range tt.wantBoundMethods {
|
||||
for structName, wantBoundMethods := range packageData {
|
||||
gotBoundMethods := got.BoundMethods[packageName][structName]
|
||||
if diff := cmp.Diff(wantBoundMethods, gotBoundMethods); diff != "" {
|
||||
t.Errorf("ParseDirectory() failed:\n" + diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loop over the models
|
||||
for _, packageData := range got.Models {
|
||||
for _, wantModel := range packageData {
|
||||
// Loop over the Fields
|
||||
for _, field := range wantModel.Fields {
|
||||
field.Project = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.wantBoundMethods, got.BoundMethods); diff != "" {
|
||||
t.Errorf("ParseDirectory() failed:\n" + diff)
|
||||
}
|
||||
|
||||
@@ -1120,6 +1120,36 @@ func TestParseStructLiteralSingle(t *testing.T) {
|
||||
t.Errorf("ParseDirectory() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
|
||||
// Patch the PackageDir in the wantBoundMethods
|
||||
for _, packageData := range got.BoundMethods {
|
||||
for _, boundMethods := range packageData {
|
||||
for _, boundMethod := range boundMethods {
|
||||
boundMethod.PackageDir = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loop over the things we want
|
||||
for packageName, packageData := range tt.wantBoundMethods {
|
||||
for structName, wantBoundMethods := range packageData {
|
||||
gotBoundMethods := got.BoundMethods[packageName][structName]
|
||||
if diff := cmp.Diff(wantBoundMethods, gotBoundMethods); diff != "" {
|
||||
t.Errorf("ParseDirectory() failed:\n" + diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loop over the models
|
||||
for _, packageData := range got.Models {
|
||||
for _, wantModel := range packageData {
|
||||
// Loop over the Fields
|
||||
for _, field := range wantModel.Fields {
|
||||
field.Project = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.wantBoundMethods, got.BoundMethods); diff != "" {
|
||||
t.Errorf("ParseDirectory() failed:\n" + diff)
|
||||
}
|
||||
|
||||
@@ -149,8 +149,9 @@ func TestParseVariableSingle(t *testing.T) {
|
||||
"github.com/wailsapp/wails/v3/internal/parser/testdata/variable_single_from_other_function/services": {
|
||||
"OtherService": {
|
||||
{
|
||||
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/variable_single_from_other_function/services",
|
||||
Name: "Yay",
|
||||
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/variable_single_from_other_function/services",
|
||||
Name: "Yay",
|
||||
DocComment: "Yay does this and that",
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Type: &ParameterType{
|
||||
@@ -169,7 +170,8 @@ func TestParseVariableSingle(t *testing.T) {
|
||||
wantModels: map[string]map[string]*StructDef{
|
||||
"main": {
|
||||
"Person": {
|
||||
Name: "Person",
|
||||
Name: "Person",
|
||||
DocComments: []string{"// Person is a person!", "// They have a name and an address"},
|
||||
Fields: []*Field{
|
||||
{
|
||||
Name: "Name",
|
||||
@@ -228,6 +230,36 @@ func TestParseVariableSingle(t *testing.T) {
|
||||
t.Errorf("ParseDirectory() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
|
||||
// Patch the PackageDir in the wantBoundMethods
|
||||
for _, packageData := range got.BoundMethods {
|
||||
for _, boundMethods := range packageData {
|
||||
for _, boundMethod := range boundMethods {
|
||||
boundMethod.PackageDir = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loop over the things we want
|
||||
for packageName, packageData := range tt.wantBoundMethods {
|
||||
for structName, wantBoundMethods := range packageData {
|
||||
gotBoundMethods := got.BoundMethods[packageName][structName]
|
||||
if diff := cmp.Diff(wantBoundMethods, gotBoundMethods); diff != "" {
|
||||
t.Errorf("ParseDirectory() failed:\n" + diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loop over the models
|
||||
for _, packageData := range got.Models {
|
||||
for _, wantModel := range packageData {
|
||||
// Loop over the Fields
|
||||
for _, field := range wantModel.Fields {
|
||||
field.Project = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.wantBoundMethods, got.BoundMethods); diff != "" {
|
||||
t.Errorf("ParseDirectory() failed:\n" + diff)
|
||||
}
|
||||
|
||||
@@ -1,20 +1,26 @@
|
||||
{{$pkg := .Package}}
|
||||
export namespace {{.Package}} {
|
||||
{{range $enumindex, $enumdef := .Enums}}
|
||||
{{- range $commentindex, $commentdef := .DocComments}}
|
||||
{{$commentdef -}}
|
||||
{{- $pkg := .Package}}{{- range .Imports}}
|
||||
import * as {{.PackageName}} from "{{.Path}}/models";{{- end}}
|
||||
{{- range $enumindex, $enumdef := .Enums}}
|
||||
{{- range $commentindex, $commentdef := $enumdef.DocComments}}
|
||||
{{$commentdef -}}
|
||||
{{- end}}
|
||||
export enum {{$enumdef.Name}} {
|
||||
{{- range $constindex, $constdef := .Consts}}
|
||||
{{- if $constdef.DocComments}}
|
||||
{{- range $commentindex, $commentdef := $constdef.DocComments}}
|
||||
{{$commentdef -}}
|
||||
{{- end }}
|
||||
{{- end}}
|
||||
export enum {{$enumdef.Name}} {
|
||||
{{- range $constindex, $constdef := .Consts}}
|
||||
{{- if $constdef.DocComment}}
|
||||
// {{$constdef.DocComment}}
|
||||
{{- end}}
|
||||
{{$constdef.Name}} = {{$constdef.Value}},{{end}}
|
||||
}
|
||||
{{- end}}
|
||||
{{range $name, $def := .Models}}
|
||||
export interface {{$def.Name}} { {{range $def.Fields}}
|
||||
{{.JSDef $pkg}}{{end}}
|
||||
}
|
||||
{{end}}
|
||||
{{$constdef.Name}} = {{$constdef.Value}},{{end}}
|
||||
}
|
||||
{{- end}}
|
||||
{{range $name, $def := .Models}}
|
||||
{{- if $def.DocComments}}
|
||||
{{- range $commentindex, $commentdef := $def.DocComments}}
|
||||
{{$commentdef -}}
|
||||
{{- end }}
|
||||
{{- end}}
|
||||
export interface {{$def.Name}} { {{- range $def.Fields}}
|
||||
{{.JSDef $pkg}}{{end}}
|
||||
}
|
||||
{{end}}
|
||||
@@ -1,21 +1,32 @@
|
||||
{{$pkg := .Package}}
|
||||
// Defining the {{$pkg}} namespace
|
||||
export const {{$pkg}} = {};
|
||||
{{range $enumindex, $enumdef := .Enums}}
|
||||
// Simulating the enum with an object
|
||||
{{$pkg}}.{{$enumdef.Name}} = {
|
||||
{{- if .Imports }}
|
||||
/**
|
||||
{{- range .Imports}}
|
||||
* @typedef {import('{{.Path}}/models').{{.Name -}} } {{.VarName}}
|
||||
{{- end}}
|
||||
*/
|
||||
{{end}}
|
||||
{{- range $enumindex, $enumdef := .Enums}}
|
||||
{{- range $commentindex, $commentdef := $enumdef.DocComments}}
|
||||
{{$commentdef -}}
|
||||
{{- end}}
|
||||
export const {{$enumdef.Name}} = {
|
||||
{{- range $constindex, $constdef := .Consts}}
|
||||
{{- if $constdef.DocComment}}
|
||||
// {{$constdef.DocComment}}
|
||||
{{- if $constdef.DocComments}}
|
||||
{{- range $commentindex, $commentdef := $constdef.DocComments}}
|
||||
{{$commentdef -}}
|
||||
{{- end }}
|
||||
{{- end}}
|
||||
{{$constdef.Name}}: {{$constdef.Value}},{{end}}
|
||||
};
|
||||
{{end}}
|
||||
{{- range $name, $def := .Models}}
|
||||
{{- if $def.DocComments}}
|
||||
{{- range $commentindex, $commentdef := $def.DocComments}}
|
||||
{{$commentdef -}}
|
||||
{{- end }}
|
||||
{{- end}}
|
||||
{{range $name, $def := .Models}}
|
||||
{{- if $def.DocComment}}
|
||||
// {{$def.DocComment}}
|
||||
{{- end -}}
|
||||
{{$pkg}}.{{$def.Name}} = class {
|
||||
export const {{$def.Name}} = class {
|
||||
/**
|
||||
* Creates a new {{$def.Name}} instance.
|
||||
* @constructor
|
||||
@@ -31,11 +42,11 @@ export const {{$pkg}} = {};
|
||||
/**
|
||||
* Creates a new {{$def.Name}} instance from a string or object.
|
||||
* @param {string|object} source - The source data to create a {{$def.Name}} instance from.
|
||||
* @returns {{$pkg}}.{{$def.Name}} A new {{$def.Name}} instance.
|
||||
* @returns { {{- $def.Name -}} } A new {{$def.Name}} instance.
|
||||
*/
|
||||
static createFrom(source = {}) {
|
||||
static createFrom(source) {
|
||||
let parsedSource = typeof source === 'string' ? JSON.parse(source) : source;
|
||||
return new {{$pkg}}.{{$def.Name}}(parsedSource);
|
||||
return new {{$def.Name}}(parsedSource);
|
||||
}
|
||||
};
|
||||
{{end}}
|
||||
|
||||
@@ -1,31 +1,38 @@
|
||||
{{$pkg := .Package}}
|
||||
export namespace {{.Package}} {
|
||||
{{range $enumindex, $enumdef := .Enums}}
|
||||
{{- range $commentindex, $commentdef := .DocComments}}
|
||||
{{$commentdef -}}
|
||||
{{- $pkg := .Package}}{{- range .Imports}}
|
||||
import * as {{.PackageName}} from "{{.Path}}/models";{{- end}}
|
||||
{{- range $enumindex, $enumdef := .Enums}}
|
||||
{{- range $commentindex, $commentdef := $enumdef.DocComments}}
|
||||
{{$commentdef -}}
|
||||
{{- end}}
|
||||
export enum {{$enumdef.Name}} {
|
||||
{{- range $constindex, $constdef := .Consts}}
|
||||
{{- if $constdef.DocComments}}
|
||||
{{- range $commentindex, $commentdef := $constdef.DocComments}}
|
||||
{{$commentdef -}}
|
||||
{{- end }}
|
||||
{{- end}}
|
||||
export enum {{$enumdef.Name}} {
|
||||
{{- range $constindex, $constdef := .Consts}}
|
||||
{{- if $constdef.DocComment}}
|
||||
// {{$constdef.DocComment}}
|
||||
{{- end}}
|
||||
{{$constdef.Name}} = {{$constdef.Value}},{{end}}
|
||||
}
|
||||
{{- end}}
|
||||
{{range $name, $def := .Models}}
|
||||
export class {{$def.Name}} {
|
||||
{{range $def.Fields}}{{.JSDef $pkg}}
|
||||
{{end}}
|
||||
constructor(source: Partial<{{$def.Name}}> = {}) {
|
||||
const { {{$def.DefaultValueList}} } = source; {{range $def.Fields}}
|
||||
this.{{.JSName}} = {{.JSName}};{{end}}
|
||||
}
|
||||
|
||||
static createFrom(source: string | object = {}): {{$def.Name}} {
|
||||
let parsedSource = typeof source === 'string' ? JSON.parse(source) : source;
|
||||
return new {{$def.Name}}(parsedSource as Partial<{{$def.Name}}>);
|
||||
}
|
||||
{{$constdef.Name}} = {{$constdef.Value}},{{end}}
|
||||
}
|
||||
{{- end}}
|
||||
{{range $name, $def := .Models}}
|
||||
{{- if $def.DocComments}}
|
||||
{{- range $commentindex, $commentdef := $def.DocComments}}
|
||||
{{$commentdef -}}
|
||||
{{- end }}
|
||||
{{- end}}
|
||||
export class {{$def.Name}} {
|
||||
{{- range $def.Fields}}
|
||||
{{.JSDef $pkg}}{{end}}
|
||||
|
||||
constructor(source: Partial<{{$def.Name}}> = {}) {
|
||||
const { {{- $def.DefaultValueList -}} } = source;
|
||||
{{- range $def.Fields}}
|
||||
this.{{.JSName}} = {{.JSName}};{{end}}
|
||||
}
|
||||
{{end}}
|
||||
}
|
||||
|
||||
static createFrom(source: string | object = {}): {{$def.Name}} {
|
||||
let parsedSource = typeof source === 'string' ? JSON.parse(source) : source;
|
||||
return new {{$def.Name}}(parsedSource as Partial<{{$def.Name}}>);
|
||||
}
|
||||
}
|
||||
{{end}}
|
||||
@@ -1,29 +0,0 @@
|
||||
// @ts-check
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
/**
|
||||
* @typedef {import('./models').main.Title} mainTitle
|
||||
* @typedef {import('./models').main.Person} mainPerson
|
||||
*/
|
||||
|
||||
export const GreetService = {
|
||||
|
||||
/**
|
||||
* GreetService.Greet
|
||||
* Greet does XYZ
|
||||
* @param name {string}
|
||||
* @param title {mainTitle}
|
||||
* @returns {Promise<string>}
|
||||
**/
|
||||
Greet: function(name, title) { return wails.CallByID(1411160069, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
|
||||
/**
|
||||
* GreetService.NewPerson
|
||||
* NewPerson creates a new person
|
||||
* @param name {string}
|
||||
* @returns {Promise<mainPerson>}
|
||||
**/
|
||||
NewPerson: function(name) { return wails.CallByID(1661412647, ...Array.prototype.slice.call(arguments, 0)); },
|
||||
};
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
// @ts-check
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
/**
|
||||
* @typedef {import('./models').main.Title} mainTitle
|
||||
* @typedef {import('./models').main.Person} mainPerson
|
||||
*/
|
||||
|
||||
export const GreetService = {
|
||||
|
||||
/**
|
||||
* GreetService.Greet
|
||||
* Greet does XYZ
|
||||
* @param name {string}
|
||||
* @param title {mainTitle}
|
||||
* @returns {Promise<string>}
|
||||
**/
|
||||
Greet: function(name, title) { return wails.CallByName("main.GreetService.Greet", ...Array.prototype.slice.call(arguments, 0)); },
|
||||
|
||||
/**
|
||||
* GreetService.NewPerson
|
||||
* NewPerson creates a new person
|
||||
* @param name {string}
|
||||
* @returns {Promise<mainPerson>}
|
||||
**/
|
||||
NewPerson: function(name) { return wails.CallByName("main.GreetService.NewPerson", ...Array.prototype.slice.call(arguments, 0)); },
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user