Support TS bindings. Update tests. Tidy up.

This commit is contained in:
Lea Anthony
2023-12-23 08:49:58 +11:00
parent cf7537df01
commit d1255d3a9d
75 changed files with 1824 additions and 316 deletions
+170 -58
View File
@@ -3,7 +3,6 @@ package parser
import (
"fmt"
"sort"
"strconv"
"strings"
"github.com/samber/lo"
@@ -15,29 +14,41 @@ const header = `// @ts-check
`
const headerTypescript = `// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
`
const bindingTemplate = `
/**Comments
* @function {{methodName}}* @param names {string}
* @returns {Promise<string>}
**/
`
const bindingTemplateTypescript = `Comments`
const callByID = `export function {{methodName}}({{inputs}}) {
const callByIDTypescript = `export async function {{methodName}}({{inputs}}) : {{ReturnType}} {
return wails.CallByID({{ID}}{{params}});
}
`
const callByNameTypescript = `export async function {{methodName}}({{inputs}}) : {{ReturnType}} {
return wails.CallByName("{{Name}}"{{params}});
}
`
const callByID = `export async function {{methodName}}({{inputs}}) {
return wails.CallByID({{ID}}, ...Array.prototype.slice.call(arguments, 0));
}
`
const callByName = `export function {{methodName}}({{inputs}}) {
const callByName = `export async function {{methodName}}({{inputs}}) {
return wails.CallByName("{{Name}}", ...Array.prototype.slice.call(arguments, 0));
}
`
const enumTemplate = `
export enum {{.EnumName}} {
{{.EnumValues}}
}
`
var reservedWords = []string{
"abstract",
"arguments",
@@ -219,6 +230,102 @@ func GenerateBinding(thisStructName string, method *BoundMethod, useIDs bool) (s
return result, lo.Uniq(models), externalStructs
}
func GenerateBindingTypescript(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 := bindingTemplateTypescript
if useIDs {
template += callByIDTypescript
} else {
template += callByNameTypescript
}
result := strings.ReplaceAll(template, "{{structName}}", thisStructName)
result = strings.ReplaceAll(result, "{{methodName}}", method.Name)
result = strings.ReplaceAll(result, "{{ID}}", fmt.Sprintf("%v", method.ID))
// get last part of method.Package path
parts := strings.Split(method.Package, "/")
packageName := parts[len(parts)-1]
result = strings.ReplaceAll(result, "{{Name}}", fmt.Sprintf("%v.%v.%v", packageName, thisStructName, method.Name))
comments := strings.TrimSpace(method.DocComment)
if comments != "" {
comments = "// " + comments + "\n"
}
result = strings.ReplaceAll(result, "Comments", comments)
var params string
for _, input := range method.Inputs {
inputName := sanitiseJSVarName(input.Name)
pkgName := getPackageName(input)
if pkgName != "" {
models = append(models, pkgName)
}
if input.Type.IsStruct || input.Type.IsEnum {
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 += ", " + inputName
}
result = strings.ReplaceAll(result, "{{params}}", params)
//if len(params) > 0 {
// params = "\n" + params
//}
var inputs string
for _, input := range method.Inputs {
pkgName := getPackageName(input)
if pkgName != "" {
models = append(models, pkgName)
}
inputs += sanitiseJSVarName(input.Name) + ": " + input.JSType(packageName) + ", "
}
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 = "Promise<void>"
} else {
returns = "Promise<"
for _, output := range method.Outputs {
pkgName := getPackageName(output)
if pkgName != "" {
models = append(models, pkgName)
}
jsType := output.JSType(packageName)
if jsType == "error" {
jsType = "void"
}
if output.Type.IsStruct {
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 + ", "
}
returns = strings.TrimSuffix(returns, ", ")
returns += ">"
}
result = strings.ReplaceAll(result, "{{ReturnType}}", returns)
return result, lo.Uniq(models), externalStructs
}
func getPackageName(input *Parameter) string {
if !input.Type.IsStruct {
return ""
@@ -230,37 +337,7 @@ func getPackageName(input *Parameter) string {
return result
}
func normalisePackageNames(packageNames []string) map[string]string {
// We iterate over the package names and determine if any of them
// have a forward slash. If this is the case, we assume that the
// package name is the last element of the path. If this has already
// been found, then we need to add a digit to the end of the package
// name to make it unique. We return a map of the original package
// name to the new package name.
var result = make(map[string]string)
var packagesConverted = make(map[string]struct{})
var count = 1
for _, packageName := range packageNames {
var originalPackageName = packageName
if strings.Contains(packageName, "/") {
parts := strings.Split(packageName, "/")
packageName = parts[len(parts)-1]
}
if _, ok := packagesConverted[packageName]; ok {
// We've already seen this package name. Add a digit
// to the end of the package name to make it unique
count += 1
packageName += strconv.Itoa(count)
}
packagesConverted[packageName] = struct{}{}
result[originalPackageName] = packageName
}
return result
}
func (p *Project) GenerateBindings(bindings map[string]map[string][]*BoundMethod, useIDs bool) map[string]map[string]string {
func (p *Project) GenerateBindings(bindings map[string]map[string][]*BoundMethod, useIDs bool, useTypescript bool) map[string]map[string]string {
var result = make(map[string]map[string]string)
@@ -288,7 +365,11 @@ func (p *Project) GenerateBindings(bindings map[string]map[string][]*BoundMethod
var thisBinding string
var models []string
for _, method := range methods {
thisBinding, models, namespacedStructs = GenerateBinding(structName, method, useIDs)
if useTypescript {
thisBinding, models, namespacedStructs = GenerateBindingTypescript(structName, method, useIDs)
} else {
thisBinding, models, namespacedStructs = GenerateBinding(structName, method, useIDs)
}
// Merge the namespaced structs
allNamespacedStructs = mergeNamespacedStructs(allNamespacedStructs, namespacedStructs)
allModels = append(allModels, models...)
@@ -297,27 +378,58 @@ func (p *Project) GenerateBindings(bindings map[string]map[string][]*BoundMethod
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
}
if !useTypescript {
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"
// 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]
} else {
// Generate imports instead of typedefs
imports := ""
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]
if namePrefix != "" {
imports += "import {" + thisStructName + " as " + namePrefix + structInfo.Name + "} from '" + relativePackageDir + "/models';\n"
} else {
imports += "import {" + thisStructName + "} from '" + relativePackageDir + "/models';\n"
}
}
}
imports += "\n"
result[relativePackageDir][structName] = imports + result[relativePackageDir][structName]
}
typedefs += " */\n"
result[relativePackageDir][structName] = typedefs + result[relativePackageDir][structName]
}
result[relativePackageDir][structName] = header + result[relativePackageDir][structName]
if useTypescript {
result[relativePackageDir][structName] = headerTypescript + result[relativePackageDir][structName]
} else {
result[relativePackageDir][structName] = header + result[relativePackageDir][structName]
}
}
}
File diff suppressed because it is too large Load Diff
+6 -2
View File
@@ -331,7 +331,7 @@ func GenerateBindingsAndModels(options *flags.GenerateBindingsOptions) error {
}
p.Stats.NumMethods = len(p.BoundMethods)
p.outputDirectory = options.OutputDirectory
generatedMethods := p.GenerateBindings(p.BoundMethods, options.UseIDs)
generatedMethods := p.GenerateBindings(p.BoundMethods, options.UseIDs, options.TS)
for pkg, structs := range generatedMethods {
// Write the directory
err = os.MkdirAll(filepath.Join(options.OutputDirectory, pkg), 0755)
@@ -340,7 +340,11 @@ func GenerateBindingsAndModels(options *flags.GenerateBindingsOptions) error {
}
// Write the files
for structName, text := range structs {
err = os.WriteFile(filepath.Join(options.OutputDirectory, pkg, structName+".js"), []byte(text), 0644)
filename := structName + ".js"
if options.TS {
filename = structName + ".ts"
}
err = os.WriteFile(filepath.Join(options.OutputDirectory, pkg, filename), []byte(text), 0644)
if err != nil {
return err
}
@@ -14,7 +14,7 @@
* @param title {Title}
* @returns {Promise<string>}
**/
export function Greet(name, title) {
export async function Greet(name, title) {
return wails.CallByID(1411160069, ...Array.prototype.slice.call(arguments, 0));
}
@@ -24,6 +24,6 @@ export function Greet(name, title) {
* @param name {string}
* @returns {Promise<Person>}
**/
export function NewPerson(name) {
export async function NewPerson(name) {
return wails.CallByID(1661412647, ...Array.prototype.slice.call(arguments, 0));
}
@@ -14,7 +14,7 @@
* @param title {Title}
* @returns {Promise<string>}
**/
export function Greet(name, title) {
export async function Greet(name, title) {
return wails.CallByName("main.GreetService.Greet", ...Array.prototype.slice.call(arguments, 0));
}
@@ -24,6 +24,6 @@ export function Greet(name, title) {
* @param name {string}
* @returns {Promise<Person>}
**/
export function NewPerson(name) {
export async function NewPerson(name) {
return wails.CallByName("main.GreetService.NewPerson", ...Array.prototype.slice.call(arguments, 0));
}
@@ -0,0 +1,16 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import {Person} from './models';
import {Title} from './models';
// Greet does XYZ
export async function Greet(name: string, title: Title) : Promise<string> {
return wails.CallByName("main.GreetService.Greet", name, title);
}
// NewPerson creates a new person
export async function NewPerson(name: string) : Promise<Person> {
return wails.CallByName("main.GreetService.NewPerson", name);
}
@@ -0,0 +1,16 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import {Person} from './models';
import {Title} from './models';
// Greet does XYZ
export async function Greet(name: string, title: Title) : Promise<string> {
return wails.CallByID(1411160069, name, title);
}
// NewPerson creates a new person
export async function NewPerson(name: string) : Promise<Person> {
return wails.CallByID(1661412647, name);
}
@@ -13,6 +13,6 @@
* @param title {servicesTitle}
* @returns {Promise<string>}
**/
export function Greet(name, title) {
export async function Greet(name, title) {
return wails.CallByID(1411160069, ...Array.prototype.slice.call(arguments, 0));
}
@@ -13,6 +13,6 @@
* @param title {servicesTitle}
* @returns {Promise<string>}
**/
export function Greet(name, title) {
export async function Greet(name, title) {
return wails.CallByName("main.GreetService.Greet", ...Array.prototype.slice.call(arguments, 0));
}
@@ -0,0 +1,10 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import {Title as servicesTitle} from '../services/models';
// Greet does XYZ
export async function Greet(name: string, title: servicesTitle) : Promise<string> {
return wails.CallByName("main.GreetService.Greet", name, title);
}
@@ -0,0 +1,10 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import {Title as servicesTitle} from '../services/models';
// Greet does XYZ
export async function Greet(name: string, title: servicesTitle) : Promise<string> {
return wails.CallByID(1411160069, name, title);
}
@@ -12,7 +12,7 @@
* @param name {string}
* @returns {Promise<string>}
**/
export function Greet(name) {
export async function Greet(name) {
return wails.CallByID(1411160069, ...Array.prototype.slice.call(arguments, 0));
}
@@ -22,6 +22,6 @@ export function Greet(name) {
* @param name {string}
* @returns {Promise<Person>}
**/
export function NewPerson(name) {
export async function NewPerson(name) {
return wails.CallByID(1661412647, ...Array.prototype.slice.call(arguments, 0));
}
@@ -12,7 +12,7 @@
* @param name {string}
* @returns {Promise<string>}
**/
export function Greet(name) {
export async function Greet(name) {
return wails.CallByName("main.GreetService.Greet", ...Array.prototype.slice.call(arguments, 0));
}
@@ -22,6 +22,6 @@ export function Greet(name) {
* @param name {string}
* @returns {Promise<Person>}
**/
export function NewPerson(name) {
export async function NewPerson(name) {
return wails.CallByName("main.GreetService.NewPerson", ...Array.prototype.slice.call(arguments, 0));
}
@@ -0,0 +1,15 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import {Person} from './models';
// Greet does XYZ
export async function Greet(name: string) : Promise<string> {
return wails.CallByName("main.GreetService.Greet", name);
}
// NewPerson creates a new person
export async function NewPerson(name: string) : Promise<Person> {
return wails.CallByName("main.GreetService.NewPerson", name);
}
@@ -0,0 +1,15 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import {Person} from './models';
// Greet does XYZ
export async function Greet(name: string) : Promise<string> {
return wails.CallByID(1411160069, name);
}
// NewPerson creates a new person
export async function NewPerson(name: string) : Promise<Person> {
return wails.CallByID(1661412647, name);
}
@@ -11,6 +11,6 @@
* @function Yay
* @returns {Promise<Address>}
**/
export function Yay() {
export async function Yay() {
return wails.CallByID(1592414782, ...Array.prototype.slice.call(arguments, 0));
}
@@ -11,6 +11,6 @@
* @function Yay
* @returns {Promise<Address>}
**/
export function Yay() {
export async function Yay() {
return wails.CallByName("services.OtherService.Yay", ...Array.prototype.slice.call(arguments, 0));
}
@@ -0,0 +1,10 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import {Address} from './models';
// Yay does this and that
export async function Yay() : Promise<Address> {
return wails.CallByName("services.OtherService.Yay");
}
@@ -0,0 +1,10 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import {Address} from './models';
// Yay does this and that
export async function Yay() : Promise<Address> {
return wails.CallByID(1592414782);
}
@@ -12,7 +12,7 @@
* @param name {string}
* @returns {Promise<string>}
**/
export function Greet(name) {
export async function Greet(name) {
return wails.CallByID(1411160069, ...Array.prototype.slice.call(arguments, 0));
}
@@ -22,6 +22,6 @@ export function Greet(name) {
* @param name {string}
* @returns {Promise<Person>}
**/
export function NewPerson(name) {
export async function NewPerson(name) {
return wails.CallByID(1661412647, ...Array.prototype.slice.call(arguments, 0));
}

Some files were not shown because too many files have changed in this diff Show More