diff --git a/v3/cmd/wails3/main.go b/v3/cmd/wails3/main.go index 27b4dbb1d..14bb427ad 100644 --- a/v3/cmd/wails3/main.go +++ b/v3/cmd/wails3/main.go @@ -41,6 +41,7 @@ func main() { generate.NewSubCommandFunction("icons", "Generate icons", commands.GenerateIcons) generate.NewSubCommandFunction("syso", "Generate Windows .syso file", commands.GenerateSyso) generate.NewSubCommandFunction("bindings", "Generate bindings + models", commands.GenerateBindings) + generate.NewSubCommandFunction("constants", "Generate JS constants from Go", commands.GenerateConstants) plugin := app.NewSubCommand("plugin", "Plugin tools") //plugin.NewSubCommandFunction("list", "List plugins", commands.PluginList) plugin.NewSubCommandFunction("init", "Initialise a new plugin", commands.PluginInit) diff --git a/v3/internal/commands/constants.go b/v3/internal/commands/constants.go new file mode 100644 index 000000000..d44e5258b --- /dev/null +++ b/v3/internal/commands/constants.go @@ -0,0 +1,25 @@ +package commands + +import ( + "github.com/wailsapp/wails/v3/internal/parser" + "os" +) + +type GenerateConstantsOptions struct { + ConstantsFilename string `name:"f" description:"The filename for the models file" default:"constants.go"` + OutputFilename string `name:"o" description:"The output file" default:"constants.js"` +} + +func GenerateConstants(options *GenerateConstantsOptions) error { + goData, err := os.ReadFile(options.ConstantsFilename) + if err != nil { + return err + } + + result, err := parser.GenerateConstants(goData) + if err != nil { + return err + } + + return os.WriteFile(options.OutputFilename, []byte(result), 0644) +} diff --git a/v3/internal/parser/constants.go b/v3/internal/parser/constants.go new file mode 100644 index 000000000..cfd5c4ab8 --- /dev/null +++ b/v3/internal/parser/constants.go @@ -0,0 +1,54 @@ +package parser + +import ( + "fmt" + "go/ast" + "go/parser" + "go/token" + "strings" +) + +func GenerateConstants(goData []byte) (string, error) { + + // Create a new token file set and parser + fs := token.NewFileSet() + f, err := parser.ParseFile(fs, "", goData, parser.AllErrors) + if err != nil { + return "", err + } + + // Extract constant declarations and generate JavaScript constants + var jsConstants []string + for _, decl := range f.Decls { + if gd, ok := decl.(*ast.GenDecl); ok && gd.Tok == token.CONST { + for _, spec := range gd.Specs { + if vs, ok := spec.(*ast.ValueSpec); ok { + for i, name := range vs.Names { + value := vs.Values[i] + if value != nil { + jsConstants = append(jsConstants, fmt.Sprintf("export const %s = %s;", name.Name, jsValue(value))) + } + } + } + } + } + } + + // Join the JavaScript constants into a single string + jsCode := strings.Join(jsConstants, "\n") + + return jsCode, nil +} + +func jsValue(expr ast.Expr) string { + // Implement conversion from Go constant value to JavaScript value here. + // You can add more cases for different types if needed. + switch e := expr.(type) { + case *ast.BasicLit: + return e.Value + case *ast.Ident: + return e.Name + default: + return "" + } +} diff --git a/v3/internal/parser/constants_test.go b/v3/internal/parser/constants_test.go new file mode 100644 index 000000000..870ca4ecf --- /dev/null +++ b/v3/internal/parser/constants_test.go @@ -0,0 +1,55 @@ +package parser + +import "testing" + +func TestGenerateConstants(t *testing.T) { + tests := []struct { + name string + goData []byte + want string + wantErr bool + }{ + { + name: "int", + goData: []byte(`package test +const one = 1`), + want: "export const one = 1;", + wantErr: false, + }, + { + name: "float", + goData: []byte(`package test +const one_point_five = 1.5`), + want: "export const one_point_five = 1.5;", + wantErr: false, + }, + { + name: "string", + goData: []byte(`package test +const one_as_a_string = "1"`), + want: `export const one_as_a_string = "1";`, + wantErr: false, + }, + { + name: "nested", + goData: []byte(`package test +const ( + one_as_a_string = "1" +)`), + want: `export const one_as_a_string = "1";`, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := GenerateConstants(tt.goData) + if (err != nil) != tt.wantErr { + t.Errorf("GenerateConstants() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("GenerateConstants() got = %v, want %v", got, tt.want) + } + }) + } +}