mirror of
https://github.com/wailsapp/wails.git
synced 2026-03-14 14:45:49 +01:00
Merge faa87bc830 into 4d0abeb37c
This commit is contained in:
commit
6f1ca75aaf
10 changed files with 129 additions and 55 deletions
|
|
@ -48,11 +48,12 @@ func generateModule(f *flags.GenerateModule) error {
|
|||
}
|
||||
|
||||
_, err = bindings.GenerateBindings(bindings.Options{
|
||||
Compiler: f.Compiler,
|
||||
Tags: buildTags,
|
||||
TsPrefix: projectConfig.Bindings.TsGeneration.Prefix,
|
||||
TsSuffix: projectConfig.Bindings.TsGeneration.Suffix,
|
||||
TsOutputType: projectConfig.Bindings.TsGeneration.OutputType,
|
||||
Compiler: f.Compiler,
|
||||
Tags: buildTags,
|
||||
TsPrefix: projectConfig.Bindings.TsGeneration.Prefix,
|
||||
TsSuffix: projectConfig.Bindings.TsGeneration.Suffix,
|
||||
TsOutputType: projectConfig.Bindings.TsGeneration.OutputType,
|
||||
UseNullableSlices: projectConfig.Bindings.TsGeneration.UseNullableSlices,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ func (a *App) Run() error {
|
|||
var tsPrefixFlag *string
|
||||
var tsPostfixFlag *string
|
||||
var tsOutputTypeFlag *string
|
||||
var useNullableSlicesFlag *bool
|
||||
|
||||
tsPrefix := os.Getenv("tsprefix")
|
||||
if tsPrefix == "" {
|
||||
|
|
@ -48,6 +49,12 @@ func (a *App) Run() error {
|
|||
tsOutputTypeFlag = bindingFlags.String("tsoutputtype", "", "Output type for generated typescript entities (classes|interfaces)")
|
||||
}
|
||||
|
||||
useNullableSlicesEnv := os.Getenv("usenullableslices")
|
||||
useNullableSlices := useNullableSlicesEnv == "true"
|
||||
if useNullableSlicesEnv == "" {
|
||||
useNullableSlicesFlag = bindingFlags.Bool("usenullableslices", false, "Generate nullable slice types (Type[] | null)")
|
||||
}
|
||||
|
||||
_ = bindingFlags.Parse(os.Args[1:])
|
||||
if tsPrefixFlag != nil {
|
||||
tsPrefix = *tsPrefixFlag
|
||||
|
|
@ -58,12 +65,16 @@ func (a *App) Run() error {
|
|||
if tsOutputTypeFlag != nil {
|
||||
tsOutputType = *tsOutputTypeFlag
|
||||
}
|
||||
if useNullableSlicesFlag != nil {
|
||||
useNullableSlices = *useNullableSlicesFlag
|
||||
}
|
||||
|
||||
appBindings := binding.NewBindings(a.logger, a.options.Bind, bindingExemptions, IsObfuscated(), a.options.EnumBind)
|
||||
|
||||
appBindings.SetTsPrefix(tsPrefix)
|
||||
appBindings.SetTsSuffix(tsSuffix)
|
||||
appBindings.SetOutputType(tsOutputType)
|
||||
appBindings.SetUseNullableSlices(useNullableSlices)
|
||||
|
||||
err := generateBindings(appBindings)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -25,10 +25,11 @@ type Bindings struct {
|
|||
|
||||
structsToGenerateTS map[string]map[string]interface{}
|
||||
enumsToGenerateTS map[string]map[string]interface{}
|
||||
tsPrefix string
|
||||
tsSuffix string
|
||||
tsInterface bool
|
||||
obfuscate bool
|
||||
tsPrefix string
|
||||
tsSuffix string
|
||||
tsInterface bool
|
||||
obfuscate bool
|
||||
useNullableSlices bool
|
||||
}
|
||||
|
||||
// NewBindings returns a new Bindings object
|
||||
|
|
@ -101,6 +102,7 @@ func (b *Bindings) GenerateModels() ([]byte, error) {
|
|||
w.WithPrefix(b.tsPrefix)
|
||||
w.WithSuffix(b.tsSuffix)
|
||||
w.WithInterface(b.tsInterface)
|
||||
w.WithUseNullableSlices(b.useNullableSlices)
|
||||
w.Namespace = packageName
|
||||
w.WithBackupDir("")
|
||||
w.KnownStructs = allStructNames
|
||||
|
|
@ -161,6 +163,7 @@ func (b *Bindings) GenerateModels() ([]byte, error) {
|
|||
w.WithPrefix(b.tsPrefix)
|
||||
w.WithSuffix(b.tsSuffix)
|
||||
w.WithInterface(b.tsInterface)
|
||||
w.WithUseNullableSlices(b.useNullableSlices)
|
||||
w.Namespace = packageName
|
||||
w.WithBackupDir("")
|
||||
|
||||
|
|
@ -328,6 +331,11 @@ func (b *Bindings) SetOutputType(outputType string) *Bindings {
|
|||
return b
|
||||
}
|
||||
|
||||
func (b *Bindings) SetUseNullableSlices(v bool) *Bindings {
|
||||
b.useNullableSlices = v
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Bindings) getAllStructNames() *slicer.StringSlicer {
|
||||
var result slicer.StringSlicer
|
||||
for packageName, structsToGenerate := range b.structsToGenerateTS {
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ func (b *Bindings) GenerateGoBindings(baseDir string) error {
|
|||
for count, input := range methodDetails.Inputs {
|
||||
arg := fmt.Sprintf("arg%d", count+1)
|
||||
entityName := entityFullReturnType(input.TypeName, b.tsPrefix, b.tsSuffix, &importNamespaces)
|
||||
args.Add(arg + ":" + goTypeToTypescriptType(entityName, &importNamespaces))
|
||||
args.Add(arg + ":" + b.goTypeToTypescriptType(entityName, &importNamespaces))
|
||||
}
|
||||
tsBody.WriteString(args.Join(",") + "):")
|
||||
// now build Typescript return types
|
||||
|
|
@ -108,11 +108,11 @@ func (b *Bindings) GenerateGoBindings(baseDir string) error {
|
|||
returnType = "Promise<void>"
|
||||
} else {
|
||||
outputTypeName := entityFullReturnType(methodDetails.Outputs[0].TypeName, b.tsPrefix, b.tsSuffix, &importNamespaces)
|
||||
firstType := goTypeToTypescriptType(outputTypeName, &importNamespaces)
|
||||
firstType := b.goTypeToTypescriptType(outputTypeName, &importNamespaces)
|
||||
returnType = "Promise<" + firstType
|
||||
if methodDetails.OutputCount() == 2 && methodDetails.Outputs[1].TypeName != "error" {
|
||||
outputTypeName = entityFullReturnType(methodDetails.Outputs[1].TypeName, b.tsPrefix, b.tsSuffix, &importNamespaces)
|
||||
secondType := goTypeToTypescriptType(outputTypeName, &importNamespaces)
|
||||
secondType := b.goTypeToTypescriptType(outputTypeName, &importNamespaces)
|
||||
returnType += "|" + secondType
|
||||
}
|
||||
returnType += ">"
|
||||
|
|
@ -175,7 +175,7 @@ var (
|
|||
jsVariableUnsafeChars = regexp.MustCompile(`[^A-Za-z0-9_]`)
|
||||
)
|
||||
|
||||
func arrayifyValue(valueArray string, valueType string) string {
|
||||
func arrayifyValue(valueArray string, valueType string, useNullableSlices bool) string {
|
||||
valueType = strings.ReplaceAll(valueType, "*", "")
|
||||
gidx := strings.IndexRune(valueType, '[')
|
||||
if gidx > 0 { // its a generic type
|
||||
|
|
@ -187,23 +187,20 @@ func arrayifyValue(valueArray string, valueType string) string {
|
|||
return valueType
|
||||
}
|
||||
|
||||
return "Array<" + valueType + ">"
|
||||
result := "Array<" + valueType + ">"
|
||||
if useNullableSlices {
|
||||
result += " | null"
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func goTypeToJSDocType(input string, importNamespaces *slicer.StringSlicer) string {
|
||||
func (b *Bindings) goTypeToJSDocType(input string, importNamespaces *slicer.StringSlicer) string {
|
||||
matches := mapRegex.FindStringSubmatch(input)
|
||||
keyPackage := matches[keyPackageIndex]
|
||||
keyType := matches[keyTypeIndex]
|
||||
valueArray := matches[valueArrayIndex]
|
||||
valuePackage := matches[valuePackageIndex]
|
||||
valueType := matches[valueTypeIndex]
|
||||
// fmt.Printf("input=%s, keyPackage=%s, keyType=%s, valueArray=%s, valuePackage=%s, valueType=%s\n",
|
||||
// input,
|
||||
// keyPackage,
|
||||
// keyType,
|
||||
// valueArray,
|
||||
// valuePackage,
|
||||
// valueType)
|
||||
|
||||
// byte array is special case
|
||||
if valueArray == "[]" && valueType == "byte" {
|
||||
|
|
@ -222,20 +219,20 @@ func goTypeToJSDocType(input string, importNamespaces *slicer.StringSlicer) stri
|
|||
key := fullyQualifiedName(keyPackage, keyType)
|
||||
var value string
|
||||
if strings.HasPrefix(valueType, "map") {
|
||||
value = goTypeToJSDocType(valueType, importNamespaces)
|
||||
value = b.goTypeToJSDocType(valueType, importNamespaces)
|
||||
} else {
|
||||
value = fullyQualifiedName(valuePackage, valueType)
|
||||
}
|
||||
|
||||
if len(key) > 0 {
|
||||
return fmt.Sprintf("Record<%s, %s>", key, arrayifyValue(valueArray, value))
|
||||
return fmt.Sprintf("Record<%s, %s>", key, arrayifyValue(valueArray, value, b.useNullableSlices))
|
||||
}
|
||||
|
||||
return arrayifyValue(valueArray, value)
|
||||
return arrayifyValue(valueArray, value, b.useNullableSlices)
|
||||
}
|
||||
|
||||
func goTypeToTypescriptType(input string, importNamespaces *slicer.StringSlicer) string {
|
||||
return goTypeToJSDocType(input, importNamespaces)
|
||||
func (b *Bindings) goTypeToTypescriptType(input string, importNamespaces *slicer.StringSlicer) string {
|
||||
return b.goTypeToJSDocType(input, importNamespaces)
|
||||
}
|
||||
|
||||
func entityFullReturnType(input, prefix, suffix string, importNamespaces *slicer.StringSlicer) string {
|
||||
|
|
|
|||
|
|
@ -139,10 +139,44 @@ func Test_goTypeToJSDocType(t *testing.T) {
|
|||
want: "main.ListData_net_http_Request_",
|
||||
},
|
||||
}
|
||||
b := &Bindings{}
|
||||
var importNamespaces slicer.StringSlicer
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := goTypeToJSDocType(tt.input, &importNamespaces); got != tt.want {
|
||||
if got := b.goTypeToJSDocType(tt.input, &importNamespaces); got != tt.want {
|
||||
t.Errorf("goTypeToJSDocType() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_goTypeToJSDocType_nullable(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "[]int nullable",
|
||||
input: "[]int",
|
||||
want: "Array<number> | null",
|
||||
},
|
||||
{
|
||||
name: "[]bool nullable",
|
||||
input: "[]bool",
|
||||
want: "Array<boolean> | null",
|
||||
},
|
||||
{
|
||||
name: "[]byte still string",
|
||||
input: "[]byte",
|
||||
want: "string",
|
||||
},
|
||||
}
|
||||
b := &Bindings{useNullableSlices: true}
|
||||
var importNamespaces slicer.StringSlicer
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := b.goTypeToJSDocType(tt.input, &importNamespaces); got != tt.want {
|
||||
t.Errorf("goTypeToJSDocType() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -251,9 +251,10 @@ type Bindings struct {
|
|||
}
|
||||
|
||||
type TsGeneration struct {
|
||||
Prefix string `json:"prefix"`
|
||||
Suffix string `json:"suffix"`
|
||||
OutputType string `json:"outputType"`
|
||||
Prefix string `json:"prefix"`
|
||||
Suffix string `json:"suffix"`
|
||||
OutputType string `json:"outputType"`
|
||||
UseNullableSlices bool `json:"useNullableSlices"`
|
||||
}
|
||||
|
||||
// Parse the given JSON data into a Project struct
|
||||
|
|
|
|||
|
|
@ -116,9 +116,10 @@ type TypeScriptify struct {
|
|||
// throwaway, used when converting
|
||||
alreadyConverted map[string]bool
|
||||
|
||||
Namespace string
|
||||
KnownStructs *slicer.StringSlicer
|
||||
KnownEnums *slicer.StringSlicer
|
||||
Namespace string
|
||||
KnownStructs *slicer.StringSlicer
|
||||
KnownEnums *slicer.StringSlicer
|
||||
UseNullableSlices bool
|
||||
}
|
||||
|
||||
func New() *TypeScriptify {
|
||||
|
|
@ -253,6 +254,11 @@ func (t *TypeScriptify) WithSuffix(s string) *TypeScriptify {
|
|||
return t
|
||||
}
|
||||
|
||||
func (t *TypeScriptify) WithUseNullableSlices(v bool) *TypeScriptify {
|
||||
t.UseNullableSlices = v
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *TypeScriptify) Add(obj interface{}) *TypeScriptify {
|
||||
switch ty := obj.(type) {
|
||||
case StructType:
|
||||
|
|
@ -658,11 +664,12 @@ func (t *TypeScriptify) convertType(depth int, typeOf reflect.Type, customCode m
|
|||
result = "export " + result
|
||||
}
|
||||
builder := typeScriptClassBuilder{
|
||||
types: t.kinds,
|
||||
indent: t.Indent,
|
||||
prefix: t.Prefix,
|
||||
suffix: t.Suffix,
|
||||
namespace: t.Namespace,
|
||||
types: t.kinds,
|
||||
indent: t.Indent,
|
||||
prefix: t.Prefix,
|
||||
suffix: t.Suffix,
|
||||
namespace: t.Namespace,
|
||||
useNullableSlices: t.UseNullableSlices,
|
||||
}
|
||||
|
||||
for _, field := range fields {
|
||||
|
|
@ -846,6 +853,7 @@ type typeScriptClassBuilder struct {
|
|||
constructorBody []string
|
||||
prefix, suffix string
|
||||
namespace string
|
||||
useNullableSlices bool
|
||||
}
|
||||
|
||||
func (t *typeScriptClassBuilder) AddSimpleArrayField(fieldName string, field reflect.StructField, arrayDepth int, opts TypeOptions) error {
|
||||
|
|
@ -863,7 +871,11 @@ func (t *typeScriptClassBuilder) AddSimpleArrayField(fieldName string, field ref
|
|||
t.addInitializerFieldLine(strippedFieldName, fmt.Sprintf("source[\"%s\"]", strippedFieldName))
|
||||
return nil
|
||||
} else if len(typeScriptType) > 0 {
|
||||
t.addField(fieldName, fmt.Sprint(typeScriptType, strings.Repeat("[]", arrayDepth)), false)
|
||||
nullableSuffix := ""
|
||||
if t.useNullableSlices {
|
||||
nullableSuffix = " | null"
|
||||
}
|
||||
t.addField(fieldName, fmt.Sprint(typeScriptType, strings.Repeat("[]", arrayDepth), nullableSuffix), false)
|
||||
t.addInitializerFieldLine(strippedFieldName, fmt.Sprintf("source[\"%s\"]", strippedFieldName))
|
||||
return nil
|
||||
}
|
||||
|
|
@ -936,7 +948,11 @@ func (t *typeScriptClassBuilder) AddArrayOfStructsField(fieldName string, field
|
|||
fieldType = field.Type.Elem().String()
|
||||
}
|
||||
strippedFieldName := strings.ReplaceAll(fieldName, "?", "")
|
||||
t.addField(fieldName, fmt.Sprint(t.prefix+fieldType+t.suffix, strings.Repeat("[]", arrayDepth)), false)
|
||||
nullableSuffix := ""
|
||||
if t.useNullableSlices {
|
||||
nullableSuffix = " | null"
|
||||
}
|
||||
t.addField(fieldName, fmt.Sprint(t.prefix+fieldType+t.suffix, strings.Repeat("[]", arrayDepth), nullableSuffix), false)
|
||||
t.addInitializerFieldLine(strippedFieldName, fmt.Sprintf("this.convertValues(source[\"%s\"], %s)", strippedFieldName, t.prefix+fieldType+t.suffix))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,14 +15,15 @@ import (
|
|||
|
||||
// Options for generating bindings
|
||||
type Options struct {
|
||||
Filename string
|
||||
Tags []string
|
||||
ProjectDirectory string
|
||||
Compiler string
|
||||
GoModTidy bool
|
||||
TsPrefix string
|
||||
TsSuffix string
|
||||
TsOutputType string
|
||||
Filename string
|
||||
Tags []string
|
||||
ProjectDirectory string
|
||||
Compiler string
|
||||
GoModTidy bool
|
||||
TsPrefix string
|
||||
TsSuffix string
|
||||
TsOutputType string
|
||||
UseNullableSlices bool
|
||||
}
|
||||
|
||||
// GenerateBindings generates bindings for the Wails project in the given ProjectDirectory.
|
||||
|
|
@ -83,6 +84,9 @@ func GenerateBindings(options Options) (string, error) {
|
|||
env = shell.SetEnv(env, "tsprefix", options.TsPrefix)
|
||||
env = shell.SetEnv(env, "tssuffix", options.TsSuffix)
|
||||
env = shell.SetEnv(env, "tsoutputtype", options.TsOutputType)
|
||||
if options.UseNullableSlices {
|
||||
env = shell.SetEnv(env, "usenullableslices", "true")
|
||||
}
|
||||
|
||||
stdout, stderr, err = shell.RunCommandWithEnv(env, workingDirectory, filename)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -231,12 +231,13 @@ func GenerateBindings(buildOptions *Options) error {
|
|||
|
||||
// Generate Bindings
|
||||
output, err := bindings.GenerateBindings(bindings.Options{
|
||||
Compiler: buildOptions.Compiler,
|
||||
Tags: buildOptions.UserTags,
|
||||
GoModTidy: !buildOptions.SkipModTidy,
|
||||
TsPrefix: buildOptions.ProjectData.Bindings.TsGeneration.Prefix,
|
||||
TsSuffix: buildOptions.ProjectData.Bindings.TsGeneration.Suffix,
|
||||
TsOutputType: buildOptions.ProjectData.Bindings.TsGeneration.OutputType,
|
||||
Compiler: buildOptions.Compiler,
|
||||
Tags: buildOptions.UserTags,
|
||||
GoModTidy: !buildOptions.SkipModTidy,
|
||||
TsPrefix: buildOptions.ProjectData.Bindings.TsGeneration.Prefix,
|
||||
TsSuffix: buildOptions.ProjectData.Bindings.TsGeneration.Suffix,
|
||||
TsOutputType: buildOptions.ProjectData.Bindings.TsGeneration.OutputType,
|
||||
UseNullableSlices: buildOptions.ProjectData.Bindings.TsGeneration.UseNullableSlices,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
### Added
|
||||
|
||||
- Added `nullableSlices` opt-in to allow nullable array type generation in [#4920](https://github.com/wailsapp/wails/pull/4920)
|
||||
- Add origin verification for bindings by @APshenkin in [PR](https://github.com/wailsapp/wails/pull/4480)
|
||||
- Configure Vite timeout by @leaanthony in [PR](https://github.com/wailsapp/wails/pull/4374)
|
||||
- Added `ContentProtection` option to allow hiding the application window from screen sharing software [#4241](https://github.com/wailsapp/wails/pull/4241) by [@Taiterbase](https://github.com/Taiterbase)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue