mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-02 15:11:53 +08:00
Feature/v3 parser: expand TS model generation tests & some fixes (#2485)
* v3 parser: add tests for model generation
* v3 parser: use single quotes for got model.ts
* v3 parser: fixes for some failing tests
* v3 parser: misc simplification and cleanup
* v3 parser: fix model tests when no structs returned
* v3 parser: fix last failing test case
* Update contributors list
* v3 parser: update README
* Revert "Update contributors list"
This reverts commit f429d2ba89
.
* Changelog: add line about my contribution
This commit is contained in:
parent
b5f1eab59b
commit
130fab6c01
@ -53,10 +53,10 @@ This package contains the static analyser used for parsing Wails projects so tha
|
|||||||
- [x] Recursive
|
- [x] Recursive
|
||||||
- [x] Anonymous
|
- [x] Anonymous
|
||||||
- [ ] Generation of models
|
- [ ] Generation of models
|
||||||
- [ ] Scalars
|
- [x] Scalars
|
||||||
- [ ] Arrays
|
- [ ] Arrays
|
||||||
- [ ] Maps
|
- [ ] Maps
|
||||||
- [ ] Structs
|
- [x] Structs
|
||||||
- [ ] Generation of bindings
|
- [ ] Generation of bindings
|
||||||
|
|
||||||
## Limitations
|
## Limitations
|
||||||
|
@ -4,6 +4,8 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"embed"
|
"embed"
|
||||||
"io"
|
"io"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -35,13 +37,34 @@ const modelsHeader = `// @ts-check
|
|||||||
// This file is automatically generated. DO NOT EDIT
|
// This file is automatically generated. DO NOT EDIT
|
||||||
`
|
`
|
||||||
|
|
||||||
|
func pkgAlias(fullPkg string) string {
|
||||||
|
pkgParts := strings.Split(fullPkg, "/")
|
||||||
|
return pkgParts[len(pkgParts)-1]
|
||||||
|
}
|
||||||
|
|
||||||
func GenerateModels(models map[packagePath]map[structName]*StructDef) (string, error) {
|
func GenerateModels(models map[packagePath]map[structName]*StructDef) (string, error) {
|
||||||
|
if models == nil {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
var buffer bytes.Buffer
|
var buffer bytes.Buffer
|
||||||
buffer.WriteString(modelsHeader)
|
buffer.WriteString(modelsHeader)
|
||||||
for pkg, pkgModels := range models {
|
|
||||||
|
// 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
|
||||||
|
var keys []string
|
||||||
|
for pkg, _ := range models {
|
||||||
|
keys = append(keys, pkg)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(keys, func(i, j int) bool {
|
||||||
|
return pkgAlias(keys[i]) < pkgAlias(keys[j])
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, pkg := range keys {
|
||||||
err := GenerateModel(&buffer, &ModelDefinitions{
|
err := GenerateModel(&buffer, &ModelDefinitions{
|
||||||
Package: pkg,
|
Package: pkgAlias(pkg),
|
||||||
Models: pkgModels,
|
Models: models[pkg],
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -2,132 +2,76 @@ package parser
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
"strings"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
const expected = `
|
func TestGenerateModels(t *testing.T) {
|
||||||
export namespace main {
|
|
||||||
|
|
||||||
export class Person {
|
|
||||||
name: string;
|
|
||||||
parent: Person;
|
|
||||||
details: anon1;
|
|
||||||
address: package.Address;
|
|
||||||
|
|
||||||
static createFrom(source: any = {}) {
|
|
||||||
return new Person(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(source: any = {}) {
|
tests := []struct {
|
||||||
if ('string' === typeof source) {
|
dir string
|
||||||
source = JSON.parse(source);
|
want string
|
||||||
}
|
}{
|
||||||
|
{
|
||||||
this.name = source["name"]
|
"testdata/function_single",
|
||||||
this.parent = source["parent"]
|
"",
|
||||||
this.details = source["details"]
|
},
|
||||||
this.address = source["address"]
|
{
|
||||||
|
"testdata/function_from_imported_package",
|
||||||
}
|
getFile("testdata/function_from_imported_package/models.ts"),
|
||||||
}
|
},
|
||||||
|
{
|
||||||
export class anon1 {
|
"testdata/variable_single",
|
||||||
age: int;
|
"",
|
||||||
address: string;
|
},
|
||||||
|
{
|
||||||
static createFrom(source: any = {}) {
|
"testdata/variable_single_from_function",
|
||||||
return new anon1(source);
|
"",
|
||||||
}
|
},
|
||||||
|
{
|
||||||
constructor(source: any = {}) {
|
"testdata/variable_single_from_other_function",
|
||||||
if ('string' === typeof source) {
|
getFile("testdata/variable_single_from_other_function/models.ts"),
|
||||||
source = JSON.parse(source);
|
},
|
||||||
}
|
{
|
||||||
|
"testdata/struct_literal_single",
|
||||||
this.age = source["age"]
|
getFile("testdata/struct_literal_single/models.ts"),
|
||||||
this.address = source["address"]
|
},
|
||||||
|
{
|
||||||
}
|
"testdata/struct_literal_multiple",
|
||||||
}
|
"",
|
||||||
|
},
|
||||||
}
|
{
|
||||||
`
|
"testdata/struct_literal_multiple_other",
|
||||||
|
getFile("testdata/struct_literal_multiple_other/models.ts"),
|
||||||
func TestGenerateClass(t *testing.T) {
|
},
|
||||||
person := StructDef{
|
{
|
||||||
Name: "Person",
|
"testdata/struct_literal_multiple_files",
|
||||||
Fields: []*Field{
|
"",
|
||||||
{
|
|
||||||
Name: "Name",
|
|
||||||
Type: &ParameterType{
|
|
||||||
Name: "string",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "Parent",
|
|
||||||
Type: &ParameterType{
|
|
||||||
Name: "Person",
|
|
||||||
IsStruct: true,
|
|
||||||
IsPointer: true,
|
|
||||||
Package: "main",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "Details",
|
|
||||||
Type: &ParameterType{
|
|
||||||
Name: "anon1",
|
|
||||||
IsStruct: true,
|
|
||||||
Package: "main",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "Address",
|
|
||||||
Type: &ParameterType{
|
|
||||||
Name: "Address",
|
|
||||||
IsStruct: true,
|
|
||||||
IsPointer: true,
|
|
||||||
Package: "github.com/some/other/package",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
anon1 := StructDef{
|
|
||||||
Name: "anon1",
|
|
||||||
Fields: []*Field{
|
|
||||||
{
|
|
||||||
Name: "Age",
|
|
||||||
Type: &ParameterType{
|
|
||||||
Name: "int",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "Address",
|
|
||||||
Type: &ParameterType{
|
|
||||||
Name: "string",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.dir, func(t *testing.T) {
|
||||||
|
// Run parser on directory
|
||||||
|
project, err := ParseProject(tt.dir)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ParseProject() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
var builder strings.Builder
|
// Generate Models
|
||||||
models := make(map[string]*StructDef)
|
got, err := GenerateModels(project.Models)
|
||||||
models["Person"] = &person
|
if err != nil {
|
||||||
models["anon1"] = &anon1
|
t.Fatalf("GenerateModels() error = %v", err)
|
||||||
def := ModelDefinitions{
|
}
|
||||||
Package: "main",
|
|
||||||
Models: models,
|
|
||||||
}
|
|
||||||
|
|
||||||
err := GenerateModel(&builder, &def)
|
if diff := cmp.Diff(tt.want, got); diff != "" {
|
||||||
if err != nil {
|
err = os.WriteFile(filepath.Join(tt.dir, "models.got.ts"), []byte(got), 0644)
|
||||||
t.Fatal(err)
|
if err != nil {
|
||||||
}
|
t.Errorf("os.WriteFile() error = %v", err)
|
||||||
|
return
|
||||||
text := builder.String()
|
}
|
||||||
println("Built string")
|
t.Fatalf("GenerateModels() mismatch (-want +got):\n%s", diff)
|
||||||
println(text)
|
}
|
||||||
if diff := cmp.Diff(expected, text); diff != "" {
|
})
|
||||||
t.Errorf("GenerateClass() failed:\n" + diff)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,16 +90,39 @@ func (f *Field) JSName() string {
|
|||||||
return strings.ToLower(f.Name[0:1]) + f.Name[1:]
|
return strings.ToLower(f.Name[0:1]) + f.Name[1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Field) JSDef(pkg string) string {
|
// TSBuild contains the typescript to build a field for a JS object
|
||||||
name := f.JSName()
|
// via assignment for simple types or constructors for structs
|
||||||
|
func (f *Field) TSBuild(pkg string) string {
|
||||||
var result string
|
if !f.Type.IsStruct {
|
||||||
|
return fmt.Sprintf("source['%s']", f.JSName())
|
||||||
|
}
|
||||||
|
|
||||||
if f.Type.Package == "" || f.Type.Package == pkg {
|
if f.Type.Package == "" || f.Type.Package == pkg {
|
||||||
result += fmt.Sprintf("%s: %s;", name, f.Type.Name)
|
return fmt.Sprintf("%s.createFrom(source['%s'])", f.Type.Name, f.JSName())
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s.%s.createFrom(source['%s'])", pkgAlias(f.Type.Package), f.Type.Name, f.JSName())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Field) JSDef(pkg string) string {
|
||||||
|
var jsType string
|
||||||
|
switch f.Type.Name {
|
||||||
|
case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "uintptr", "float32", "float64":
|
||||||
|
jsType = "number"
|
||||||
|
case "string":
|
||||||
|
jsType = "string"
|
||||||
|
case "bool":
|
||||||
|
jsType = "boolean"
|
||||||
|
default:
|
||||||
|
jsType = f.Type.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
var result string
|
||||||
|
if f.Type.Package == "" || f.Type.Package == pkg {
|
||||||
|
result += fmt.Sprintf("%s: %s;", f.JSName(), jsType)
|
||||||
} else {
|
} else {
|
||||||
parts := strings.Split(f.Type.Package, "/")
|
parts := strings.Split(f.Type.Package, "/")
|
||||||
result += fmt.Sprintf("%s: %s.%s;", name, parts[len(parts)-1], f.Type.Name)
|
result += fmt.Sprintf("%s: %s.%s;", f.JSName(), parts[len(parts)-1], jsType)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ast.IsExported(f.Name) {
|
if !ast.IsExported(f.Name) {
|
||||||
|
@ -13,7 +13,7 @@ export namespace {{.Package}} {
|
|||||||
source = JSON.parse(source);
|
source = JSON.parse(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
{{range $def.Fields}}this.{{.JSName}} = source["{{.JSName}}"]
|
{{range $def.Fields}}this.{{.JSName}} = {{.TSBuild $pkg}};
|
||||||
{{end}}
|
{{end}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
51
v3/internal/parser/testdata/function_from_imported_package/models.ts
vendored
Normal file
51
v3/internal/parser/testdata/function_from_imported_package/models.ts
vendored
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// @ts-check
|
||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
export namespace main {
|
||||||
|
|
||||||
|
export class Person {
|
||||||
|
name: string;
|
||||||
|
address: services.Address;
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new Person(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) {
|
||||||
|
source = JSON.parse(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.name = source['name'];
|
||||||
|
this.address = services.Address.createFrom(source['address']);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace services {
|
||||||
|
|
||||||
|
export class Address {
|
||||||
|
street: string;
|
||||||
|
state: string;
|
||||||
|
country: string;
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new Address(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) {
|
||||||
|
source = JSON.parse(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.street = source['street'];
|
||||||
|
this.state = source['state'];
|
||||||
|
this.country = source['country'];
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
5
v3/internal/parser/testdata/function_single/models.ts
vendored
Normal file
5
v3/internal/parser/testdata/function_single/models.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
// @ts-check
|
||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
// TODO : nothing generated yet
|
5
v3/internal/parser/testdata/struct_literal_multiple/models.ts
vendored
Normal file
5
v3/internal/parser/testdata/struct_literal_multiple/models.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
// @ts-check
|
||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
// TODO : nothing generated yet
|
5
v3/internal/parser/testdata/struct_literal_multiple_files/models.ts
vendored
Normal file
5
v3/internal/parser/testdata/struct_literal_multiple_files/models.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
// @ts-check
|
||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
// TODO : nothing generated yet
|
51
v3/internal/parser/testdata/struct_literal_multiple_other/models.ts
vendored
Normal file
51
v3/internal/parser/testdata/struct_literal_multiple_other/models.ts
vendored
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// @ts-check
|
||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
export namespace main {
|
||||||
|
|
||||||
|
export class Person {
|
||||||
|
name: string;
|
||||||
|
address: services.Address;
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new Person(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) {
|
||||||
|
source = JSON.parse(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.name = source['name'];
|
||||||
|
this.address = services.Address.createFrom(source['address']);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace services {
|
||||||
|
|
||||||
|
export class Address {
|
||||||
|
street: string;
|
||||||
|
state: string;
|
||||||
|
country: string;
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new Address(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) {
|
||||||
|
source = JSON.parse(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.street = source['street'];
|
||||||
|
this.state = source['state'];
|
||||||
|
this.country = source['country'];
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
64
v3/internal/parser/testdata/struct_literal_single/models.ts
vendored
Normal file
64
v3/internal/parser/testdata/struct_literal_single/models.ts
vendored
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// @ts-check
|
||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
export namespace main {
|
||||||
|
|
||||||
|
export class Person {
|
||||||
|
name: string;
|
||||||
|
parent: Person;
|
||||||
|
details: anon1;
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new Person(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) {
|
||||||
|
source = JSON.parse(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.name = source['name'];
|
||||||
|
this.parent = Person.createFrom(source['parent']);
|
||||||
|
this.details = anon1.createFrom(source['details']);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class anon1 {
|
||||||
|
age: number;
|
||||||
|
address: anon2;
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new anon1(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) {
|
||||||
|
source = JSON.parse(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.age = source['age'];
|
||||||
|
this.address = anon2.createFrom(source['address']);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class anon2 {
|
||||||
|
street: string;
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new anon2(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) {
|
||||||
|
source = JSON.parse(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.street = source['street'];
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
5
v3/internal/parser/testdata/variable_single/models.ts
vendored
Normal file
5
v3/internal/parser/testdata/variable_single/models.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
// @ts-check
|
||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
// TODO : nothing generated yet
|
5
v3/internal/parser/testdata/variable_single_from_function/models.ts
vendored
Normal file
5
v3/internal/parser/testdata/variable_single_from_function/models.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
// @ts-check
|
||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
// TODO : nothing generated yet
|
51
v3/internal/parser/testdata/variable_single_from_other_function/models.ts
vendored
Normal file
51
v3/internal/parser/testdata/variable_single_from_other_function/models.ts
vendored
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// @ts-check
|
||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
export namespace main {
|
||||||
|
|
||||||
|
export class Person {
|
||||||
|
name: string;
|
||||||
|
address: services.Address;
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new Person(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) {
|
||||||
|
source = JSON.parse(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.name = source['name'];
|
||||||
|
this.address = services.Address.createFrom(source['address']);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace services {
|
||||||
|
|
||||||
|
export class Address {
|
||||||
|
street: string;
|
||||||
|
state: string;
|
||||||
|
country: string;
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new Address(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) {
|
||||||
|
source = JSON.parse(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.street = source['street'];
|
||||||
|
this.state = source['state'];
|
||||||
|
this.country = source['country'];
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -14,14 +14,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- [v3] Typescript model generation using `StructDef`s from new AST-based parser. Added by @ATenderholt in [PR1](https://github.com/wailsapp/wails/pull/2428/files) and [PR2](https://github.com/wailsapp/wails/pull/2485).
|
||||||
|
|
||||||
|
|
||||||
## v2.4.1 - 2022-03-20
|
## v2.4.1 - 2022-03-20
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Support single clicks on items with `--wails-draggable: drag` again on Windows. Changed by @stffabi in [PR](https://github.com/wailsapp/wails/pull/2482)
|
- Support single clicks on items with `--wails-draggable: drag` again on Windows. Changed by @stffabi in [PR](https://github.com/wailsapp/wails/pull/2482)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Fixed panic when using `wails dev` and the AssetServer tried to log to the logger. Fixed by @stffabi in [PR](https://github.com/wailsapp/wails/pull/2481)
|
- Fixed panic when using `wails dev` and the AssetServer tried to log to the logger. Fixed by @stffabi in [PR](https://github.com/wailsapp/wails/pull/2481)
|
||||||
- Fixed compatibility with WebView2 Runtime > `110.0.1587.69` which showed a `connection refused` html page before doing a reload of the frontend. Fixed by @stffabi in [PR](https://github.com/wailsapp/wails/pull/2496)
|
- Fixed compatibility with WebView2 Runtime > `110.0.1587.69` which showed a `connection refused` html page before doing a reload of the frontend. Fixed by @stffabi in [PR](https://github.com/wailsapp/wails/pull/2496)
|
||||||
|
|
||||||
## v2.4.0 - 2022-03-08
|
## v2.4.0 - 2022-03-08
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user