diff --git a/v2/cmd/wails/internal/commands/build/build.go b/v2/cmd/wails/internal/commands/build/build.go index a55eea565..4df9aaa8d 100644 --- a/v2/cmd/wails/internal/commands/build/build.go +++ b/v2/cmd/wails/internal/commands/build/build.go @@ -2,7 +2,6 @@ package build import ( "fmt" - "github.com/wailsapp/wails/v2/pkg/commands/buildtags" "io" "os" "os/exec" @@ -11,6 +10,8 @@ import ( "text/tabwriter" "time" + "github.com/wailsapp/wails/v2/pkg/commands/buildtags" + "github.com/wailsapp/wails/v2/internal/colour" "github.com/wailsapp/wails/v2/internal/project" "github.com/wailsapp/wails/v2/internal/system" diff --git a/v2/cmd/wails/internal/commands/dev/dev.go b/v2/cmd/wails/internal/commands/dev/dev.go index 1ae464b6a..907e996b4 100644 --- a/v2/cmd/wails/internal/commands/dev/dev.go +++ b/v2/cmd/wails/internal/commands/dev/dev.go @@ -33,36 +33,13 @@ import ( "github.com/fsnotify/fsnotify" "github.com/leaanthony/clir" + "github.com/wailsapp/wails/v2/cmd/wails/internal/logutils" "github.com/wailsapp/wails/v2/internal/fs" "github.com/wailsapp/wails/v2/internal/process" "github.com/wailsapp/wails/v2/pkg/clilogger" "github.com/wailsapp/wails/v2/pkg/commands/build" ) -func LogGreen(message string, args ...interface{}) { - if len(message) == 0 { - return - } - text := fmt.Sprintf(message, args...) - println(colour.Green(text)) -} - -func LogRed(message string, args ...interface{}) { - if len(message) == 0 { - return - } - text := fmt.Sprintf(message, args...) - println(colour.Red(text)) -} - -func LogDarkYellow(message string, args ...interface{}) { - if len(message) == 0 { - return - } - text := fmt.Sprintf(message, args...) - println(colour.DarkYellow(text)) -} - func sliceToMap(input []string) map[string]struct{} { result := map[string]struct{}{} for _, value := range input { @@ -184,16 +161,18 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error { if !buildOptions.SkipBindings { if flags.verbosity == build.VERBOSE { - LogGreen("Generating Bindings...") + logutils.LogGreen("Generating Bindings...") } stdout, err := bindings.GenerateBindings(bindings.Options{ - Tags: buildOptions.UserTags, + Tags: buildOptions.UserTags, + TsPrefix: projectConfig.Bindings.TsGeneration.Prefix, + TsSuffix: projectConfig.Bindings.TsGeneration.Suffix, }) if err != nil { return err } if flags.verbosity == build.VERBOSE { - LogGreen(stdout) + logutils.LogGreen(stdout) } } @@ -238,7 +217,7 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error { } defer func() { if err := killProcessAndCleanupBinary(debugBinaryProcess, appBinary); err != nil { - LogDarkYellow("Unable to kill process and cleanup binary: %s", err) + logutils.LogDarkYellow("Unable to kill process and cleanup binary: %s", err) } }() @@ -263,17 +242,17 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error { } }(watcher) - LogGreen("Watching (sub)/directory: %s", cwd) - LogGreen("Using DevServer URL: %s", devServerURL) + logutils.LogGreen("Watching (sub)/directory: %s", cwd) + logutils.LogGreen("Using DevServer URL: %s", devServerURL) if flags.frontendDevServerURL != "" { - LogGreen("Using Frontend DevServer URL: %s", flags.frontendDevServerURL) + logutils.LogGreen("Using Frontend DevServer URL: %s", flags.frontendDevServerURL) } - LogGreen("Using reload debounce setting of %d milliseconds", flags.debounceMS) + logutils.LogGreen("Using reload debounce setting of %d milliseconds", flags.debounceMS) // Show dev server URL in terminal after 3 seconds go func() { time.Sleep(3 * time.Second) - LogGreen("\n\nTo develop in the browser and call your bound Go methods from Javascript, navigate to: %s", devServerURL) + logutils.LogGreen("\n\nTo develop in the browser and call your bound Go methods from Javascript, navigate to: %s", devServerURL) }() // Watch for changes and trigger restartApp() @@ -288,7 +267,7 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error { debugBinaryProcess = nil appBinary = "" - LogGreen("Development mode exited") + logutils.LogGreen("Development mode exited") return nil }) @@ -312,7 +291,7 @@ func killProcessAndCleanupBinary(process *process.Process, binary string) error } func runCommand(dir string, exitOnError bool, command string, args ...string) error { - LogGreen("Executing: " + command + " " + strings.Join(args, " ")) + logutils.LogGreen("Executing: " + command + " " + strings.Join(args, " ")) cmd := exec.Command(command, args...) cmd.Dir = dir output, err := cmd.CombinedOutput() @@ -460,7 +439,7 @@ func runFrontendDevWatcherCommand(frontendDirectory string, devCommand string, d } } - LogGreen("Running frontend DevWatcher command: '%s'", devCommand) + logutils.LogGreen("Running frontend DevWatcher command: '%s'", devCommand) var wg sync.WaitGroup wg.Add(1) @@ -474,7 +453,7 @@ func runFrontendDevWatcherCommand(frontendDirectory string, devCommand string, d if err := cmd.Wait(); err != nil { wasRunning := atomic.CompareAndSwapInt32(&state, stateRunning, stateStopped) if err.Error() != "exit status 1" && wasRunning { - LogRed("Error from DevWatcher '%s': %s", devCommand, err.Error()) + logutils.LogRed("Error from DevWatcher '%s': %s", devCommand, err.Error()) } } atomic.StoreInt32(&state, stateStopped) @@ -496,13 +475,13 @@ func restartApp(buildOptions *build.Options, debugBinaryProcess *process.Process appBinary, err := build.Build(buildOptions) println() if err != nil { - LogRed("Build error - " + err.Error()) + logutils.LogRed("Build error - " + err.Error()) msg := "Continuing to run current version" if debugBinaryProcess == nil { msg = "No version running, build will be retriggered as soon as changes have been detected" } - LogDarkYellow(msg) + logutils.LogDarkYellow(msg) return nil, "", nil } @@ -558,7 +537,7 @@ func doWatcherLoop(buildOptions *build.Options, debugBinaryProcess *process.Proc } thePath, err := filepath.Abs(dir) if err != nil { - LogRed("Unable to expand reloadDir '%s': %s", dir, err) + logutils.LogRed("Unable to expand reloadDir '%s': %s", dir, err) continue } dirsThatTriggerAReload = append(dirsThatTriggerAReload, thePath) @@ -585,7 +564,7 @@ func doWatcherLoop(buildOptions *build.Options, debugBinaryProcess *process.Proc quit = true } case err := <-watcher.Errors: - LogDarkYellow(err.Error()) + logutils.LogDarkYellow(err.Error()) case item := <-watcher.Events: isEligibleFile := func(fileName string) bool { // Iterate all file patterns @@ -637,7 +616,7 @@ func doWatcherLoop(buildOptions *build.Options, debugBinaryProcess *process.Proc if err != nil { buildOptions.Logger.Fatal("%s", err.Error()) } - LogGreen("Added new directory to watcher: %s", item.Name) + logutils.LogGreen("Added new directory to watcher: %s", item.Name) } } else if isEligibleFile(item.Name) { // Handle creation of new file. @@ -652,11 +631,11 @@ func doWatcherLoop(buildOptions *build.Options, debugBinaryProcess *process.Proc case <-timer.C: if rebuild { rebuild = false - LogGreen("[Rebuild triggered] files updated") + logutils.LogGreen("[Rebuild triggered] files updated") // Try and build the app newBinaryProcess, _, err := restartApp(buildOptions, debugBinaryProcess, flags, exitCodeChannel) if err != nil { - LogRed("Error during build: %s", err.Error()) + logutils.LogRed("Error during build: %s", err.Error()) continue } // If we have a new process, saveConfig it @@ -669,11 +648,11 @@ func doWatcherLoop(buildOptions *build.Options, debugBinaryProcess *process.Proc if assetDir == "" { resp, err := http.Get(assetDirURL) if err != nil { - LogRed("Error during retrieving assetdir: %s", err.Error()) + logutils.LogRed("Error during retrieving assetdir: %s", err.Error()) } else { content, err := io.ReadAll(resp.Body) if err != nil { - LogRed("Error reading assetdir from devserver: %s", err.Error()) + logutils.LogRed("Error reading assetdir from devserver: %s", err.Error()) } else { assetDir = string(content) } @@ -689,19 +668,19 @@ func doWatcherLoop(buildOptions *build.Options, debugBinaryProcess *process.Proc } } } else if len(dirsThatTriggerAReload) == 0 { - LogRed("Reloading couldn't be triggered: Please specify -assetdir or -reloaddirs") + logutils.LogRed("Reloading couldn't be triggered: Please specify -assetdir or -reloaddirs") } } if reload { reload = false _, err := http.Get(reloadURL) if err != nil { - LogRed("Error during refresh: %s", err.Error()) + logutils.LogRed("Error during refresh: %s", err.Error()) } } changedPaths = map[string]struct{}{} case <-quitChannel: - LogGreen("\nCaught quit") + logutils.LogGreen("\nCaught quit") quit = true } } diff --git a/v2/cmd/wails/internal/commands/dev/dev_other.go b/v2/cmd/wails/internal/commands/dev/dev_other.go index c93aecfe4..88e170ee3 100644 --- a/v2/cmd/wails/internal/commands/dev/dev_other.go +++ b/v2/cmd/wails/internal/commands/dev/dev_other.go @@ -7,6 +7,7 @@ import ( "os/exec" "syscall" + "github.com/wailsapp/wails/v2/cmd/wails/internal/logutils" "golang.org/x/sys/unix" ) @@ -30,7 +31,7 @@ func killProc(cmd *exec.Cmd, devCommand string) { if err == nil { err := syscall.Kill(-pgid, unix.SIGTERM) // note the minus sign if err != nil { - LogRed("Error from '%s' when attempting to kill the process: %s", devCommand, err.Error()) + logutils.LogRed("Error from '%s' when attempting to kill the process: %s", devCommand, err.Error()) } } } diff --git a/v2/cmd/wails/internal/commands/dev/stdout_scanner.go b/v2/cmd/wails/internal/commands/dev/stdout_scanner.go index 96e7ffcec..d84e4785e 100644 --- a/v2/cmd/wails/internal/commands/dev/stdout_scanner.go +++ b/v2/cmd/wails/internal/commands/dev/stdout_scanner.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/acarl005/stripansi" + "github.com/wailsapp/wails/v2/cmd/wails/internal/logutils" ) // stdoutScanner acts as a stdout target that will scan the incoming @@ -35,10 +36,10 @@ func (s *stdoutScanner) Write(data []byte) (n int, err error) { continue } viteServerURL := strings.TrimSpace(line[index+6:]) - LogGreen("Vite Server URL: %s", viteServerURL) + logutils.LogGreen("Vite Server URL: %s", viteServerURL) _, err := url.Parse(viteServerURL) if err != nil { - LogRed(err.Error()) + logutils.LogRed(err.Error()) } else { s.ViteServerURLChan <- viteServerURL } diff --git a/v2/cmd/wails/internal/commands/generate/README.md b/v2/cmd/wails/internal/commands/generate/README.md index c24eb060a..5360ae791 100644 --- a/v2/cmd/wails/internal/commands/generate/README.md +++ b/v2/cmd/wails/internal/commands/generate/README.md @@ -16,3 +16,10 @@ Generate a starter template for you to customise. | :------------- | :----------- | | -frontend | Copies all the files from the current directory into the template's `frontend` directory. Useful for converting frontend projects created by boilerplate generators. | | -q | Suppress output | + + +## Module + +`wails generate module [-h]` + +Generate TS module for your frontend diff --git a/v2/cmd/wails/internal/commands/generate/module.go b/v2/cmd/wails/internal/commands/generate/module.go index 20e7aadbd..a46d7d0e3 100644 --- a/v2/cmd/wails/internal/commands/generate/module.go +++ b/v2/cmd/wails/internal/commands/generate/module.go @@ -1,28 +1,45 @@ package generate import ( + "io" + "os" + "github.com/leaanthony/clir" + "github.com/wailsapp/wails/v2/internal/project" "github.com/wailsapp/wails/v2/pkg/commands/bindings" "github.com/wailsapp/wails/v2/pkg/commands/buildtags" - "io" ) +type generateFlags struct { + tags string +} + // AddModuleCommand adds the `module` subcommand for the `generate` command func AddModuleCommand(app *clir.Cli, parent *clir.Command, w io.Writer) error { command := parent.NewSubCommand("module", "Generate wailsjs modules") - var tags string - command.StringFlag("tags", "tags to pass to Go compiler (quoted and space separated)", &tags) + genFlags := generateFlags{} + command.StringFlag("tags", "tags to pass to Go compiler (quoted and space separated)", &genFlags.tags) command.Action(func() error { + cwd, err := os.Getwd() + if err != nil { + return err + } + projectConfig, err := project.Load(cwd) + if err != nil { + return err + } - buildTags, err := buildtags.Parse(tags) + buildTags, err := buildtags.Parse(genFlags.tags) if err != nil { return err } _, err = bindings.GenerateBindings(bindings.Options{ - Tags: buildTags, + Tags: buildTags, + TsPrefix: projectConfig.Bindings.TsGeneration.Prefix, + TsSuffix: projectConfig.Bindings.TsGeneration.Suffix, }) if err != nil { return err diff --git a/v2/cmd/wails/internal/logutils/color-logs.go b/v2/cmd/wails/internal/logutils/color-logs.go new file mode 100644 index 000000000..65553df3f --- /dev/null +++ b/v2/cmd/wails/internal/logutils/color-logs.go @@ -0,0 +1,31 @@ +package logutils + +import ( + "fmt" + + "github.com/wailsapp/wails/v2/internal/colour" +) + +func LogGreen(message string, args ...interface{}) { + if len(message) == 0 { + return + } + text := fmt.Sprintf(message, args...) + println(colour.Green(text)) +} + +func LogRed(message string, args ...interface{}) { + if len(message) == 0 { + return + } + text := fmt.Sprintf(message, args...) + println(colour.Red(text)) +} + +func LogDarkYellow(message string, args ...interface{}) { + if len(message) == 0 { + return + } + text := fmt.Sprintf(message, args...) + println(colour.DarkYellow(text)) +} diff --git a/v2/internal/app/app_bindings.go b/v2/internal/app/app_bindings.go index 14438126c..b0d192589 100644 --- a/v2/internal/app/app_bindings.go +++ b/v2/internal/app/app_bindings.go @@ -5,6 +5,7 @@ package app import ( "os" "path/filepath" + "flag" "github.com/leaanthony/gosod" "github.com/wailsapp/wails/v2/internal/binding" @@ -24,9 +25,36 @@ func (a *App) Run() error { a.options.OnDomReady, a.options.OnBeforeClose, } + + // Check for CLI Flags + bindingFlags := flag.NewFlagSet("bindings", flag.ContinueOnError) + + var tsPrefixFlag *string + var tsPostfixFlag *string + + tsPrefix := os.Getenv("tsprefix") + if tsPrefix == "" { + tsPrefixFlag = bindingFlags.String("tsprefix", "", "Prefix for generated typescript entities") + } + + tsSuffix := os.Getenv("tssuffix") + if tsSuffix == "" { + tsPostfixFlag = bindingFlags.String("tssuffix", "", "Suffix for generated typescript entities") + } + + _ = bindingFlags.Parse(os.Args[1:]) + if tsPrefixFlag != nil { + tsPrefix = *tsPrefixFlag + } + if tsPostfixFlag != nil { + tsSuffix = *tsPostfixFlag + } appBindings := binding.NewBindings(a.logger, a.options.Bind, bindingExemptions, IsObfuscated()) + appBindings.SetTsPrefix(tsPrefix) + appBindings.SetTsPostfix(tsSuffix) + err := generateBindings(appBindings) if err != nil { return err diff --git a/v2/internal/binding/binding.go b/v2/internal/binding/binding.go index a51855ac9..75b821f29 100755 --- a/v2/internal/binding/binding.go +++ b/v2/internal/binding/binding.go @@ -23,6 +23,8 @@ type Bindings struct { exemptions slicer.StringSlicer structsToGenerateTS map[string]map[string]interface{} + tsPrefix string + tsSuffix string obfuscate bool } @@ -92,6 +94,8 @@ func (b *Bindings) GenerateModels() ([]byte, error) { for packageName, structsToGenerate := range b.structsToGenerateTS { thisPackageCode := "" w := typescriptify.New() + w.WithPrefix(b.tsPrefix) + w.WithSuffix(b.tsSuffix) w.Namespace = packageName w.WithBackupDir("") w.KnownStructs = allStructNames @@ -219,6 +223,16 @@ func (b *Bindings) AddStructToGenerateTS(packageName string, structName string, } } +func (b *Bindings) SetTsPrefix(prefix string) *Bindings { + b.tsPrefix = prefix + return b +} + +func (b *Bindings) SetTsSuffix(postfix string) *Bindings { + b.tsSuffix = postfix + return b +} + func (b *Bindings) getAllStructNames() *slicer.StringSlicer { var result slicer.StringSlicer for packageName, structsToGenerate := range b.structsToGenerateTS { diff --git a/v2/internal/binding/binding_test/binding_test.go b/v2/internal/binding/binding_test/binding_test.go index 7a17ce6ca..f3d3a6611 100644 --- a/v2/internal/binding/binding_test/binding_test.go +++ b/v2/internal/binding/binding_test/binding_test.go @@ -16,6 +16,12 @@ type BindingTest struct { exemptions []interface{} want string shouldError bool + TsGenerationOptionsTest +} + +type TsGenerationOptionsTest struct { + TsPrefix string + TsSuffix string } func TestBindings_GenerateModels(t *testing.T) { @@ -30,6 +36,7 @@ func TestBindings_GenerateModels(t *testing.T) { SingleFieldTest, MultistructTest, EmptyStructTest, + GeneratedJsEntityTest, } testLogger := &logger.Logger{} @@ -40,6 +47,10 @@ func TestBindings_GenerateModels(t *testing.T) { err := b.Add(s) require.NoError(t, err) } + b.SetTsPrefix(tt.TsPrefix) + + // TODO - rename this to SetTsSuffix + b.SetTsSuffix(tt.TsSuffix) got, err := b.GenerateModels() if (err != nil) != tt.shouldError { t.Errorf("GenerateModels() error = %v, shouldError %v", err, tt.shouldError) diff --git a/v2/internal/binding/binding_test/binding_tsgeneration_test.go b/v2/internal/binding/binding_test/binding_tsgeneration_test.go new file mode 100644 index 000000000..b808ad9c6 --- /dev/null +++ b/v2/internal/binding/binding_test/binding_tsgeneration_test.go @@ -0,0 +1,41 @@ +package binding_test + +type GeneratedJsEntity struct { + Name string `json:"name"` +} + +func (s GeneratedJsEntity) Get() GeneratedJsEntity { + return s +} + +var GeneratedJsEntityTest = BindingTest{ + name: "GeneratedJsEntityTest ", + structs: []interface{}{ + &GeneratedJsEntity{}, + }, + exemptions: nil, + shouldError: false, + TsGenerationOptionsTest: TsGenerationOptionsTest{ + TsPrefix: "MY_PREFIX_", + TsSuffix: "_MY_SUFFIX", + }, + want: ` +export namespace binding_test { + + export class MY_PREFIX_GeneratedJsEntity_MY_SUFFIX { + name: string; + + static createFrom(source: any = {}) { + return new MY_PREFIX_GeneratedJsEntity_MY_SUFFIX(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.name = source["name"]; + } + } + +} + +`, +} diff --git a/v2/internal/binding/generate.go b/v2/internal/binding/generate.go index 9793e77f2..1ce2235e6 100644 --- a/v2/internal/binding/generate.go +++ b/v2/internal/binding/generate.go @@ -79,11 +79,13 @@ func (b *Bindings) GenerateGoBindings(baseDir string) error { tsBody.WriteString(args.Join(",") + "):") returnType := "Promise" if methodDetails.OutputCount() > 0 { - firstType := goTypeToTypescriptType(methodDetails.Outputs[0].TypeName, &importNamespaces) + outputTypeName := entityFullReturnType(methodDetails.Outputs[0].TypeName, b.tsPrefix, b.tsSuffix, &importNamespaces) + firstType := goTypeToTypescriptType(outputTypeName, &importNamespaces) returnType += "<" + firstType if methodDetails.OutputCount() == 2 { if methodDetails.Outputs[1].TypeName != "error" { - secondType := goTypeToTypescriptType(methodDetails.Outputs[1].TypeName, &importNamespaces) + outputTypeName = entityFullReturnType(methodDetails.Outputs[1].TypeName, b.tsPrefix, b.tsSuffix, &importNamespaces) + secondType := goTypeToTypescriptType(outputTypeName, &importNamespaces) returnType += "|" + secondType } } @@ -171,3 +173,12 @@ func goTypeToTypescriptType(input string, importNamespaces *slicer.StringSlicer) } return goTypeToJSDocType(input, importNamespaces) } + +func entityFullReturnType(input, prefix, suffix string, importNamespaces *slicer.StringSlicer) string { + if strings.ContainsRune(input, '.') { + nameSpace, returnType := getSplitReturn(input) + return nameSpace + "." + prefix + returnType + suffix + } + + return input +} diff --git a/v2/internal/binding/reflect.go b/v2/internal/binding/reflect.go index deef09ffe..66a9cf7bd 100755 --- a/v2/internal/binding/reflect.go +++ b/v2/internal/binding/reflect.go @@ -166,6 +166,12 @@ func getPackageName(in string) string { return result } +func getSplitReturn(in string) (string, string) { + result := strings.Split(in, ".") + return result[0], result[1] + +} + func hasElements(typ reflect.Type) bool { kind := typ.Kind() return kind == reflect.Ptr || kind == reflect.Array || kind == reflect.Slice || kind == reflect.Map diff --git a/v2/internal/project/project.go b/v2/internal/project/project.go index 7e2e7ffd5..016ceecb7 100644 --- a/v2/internal/project/project.go +++ b/v2/internal/project/project.go @@ -2,11 +2,12 @@ package project import ( "encoding/json" - "github.com/samber/lo" "os" "path/filepath" "runtime" "strings" + + "github.com/samber/lo" ) // Project holds the data related to a Wails project @@ -92,6 +93,8 @@ type Project struct { // Frontend directory FrontendDir string `json:"frontend:dir"` + + Bindings Bindings `json:"bindings"` } func (p *Project) GetFrontendDir() string { @@ -222,6 +225,15 @@ type Info struct { Comments *string `json:"comments"` } +type Bindings struct { + TsGeneration TsGeneration `json:"ts_generation"` +} + +type TsGeneration struct { + Prefix string `json:"prefix"` + Suffix string `json:"suffix"` +} + // Parse the given JSON data into a Project struct func Parse(projectData []byte) (*Project, error) { project := &Project{} diff --git a/v2/internal/typescriptify/js-reserved-keywords.go b/v2/internal/typescriptify/js-reserved-keywords.go new file mode 100644 index 000000000..7750fb90a --- /dev/null +++ b/v2/internal/typescriptify/js-reserved-keywords.go @@ -0,0 +1,69 @@ +package typescriptify + +var jsReservedKeywords []string = []string{ + "abstract", + "arguments", + "await", + "boolean", + "break", + "byte", + "case", + "catch", + "char", + "class", + "const", + "continue", + "debugger", + "default", + "delete", + "do", + "double", + "else", + "enum", + "eval", + "export", + "extends", + "false", + "final", + "finally", + "float", + "for", + "function", + "goto", + "if", + "implements", + "import", + "in", + "instanceof", + "int", + "interface", + "let", + "long", + "native", + "new", + "null", + "package", + "private", + "protected", + "public", + "return", + "short", + "static", + "super", + "switch", + "synchronized", + "this", + "throw", + "throws", + "transient", + "true", + "try", + "typeof", + "var", + "void", + "volotile", + "while", + "with", + "yield", + "object", +} diff --git a/v2/internal/typescriptify/typescriptify.go b/v2/internal/typescriptify/typescriptify.go index 35ee3c100..dfc77ef29 100644 --- a/v2/internal/typescriptify/typescriptify.go +++ b/v2/internal/typescriptify/typescriptify.go @@ -4,6 +4,7 @@ import ( "bufio" "fmt" "io/ioutil" + "log" "os" "path" "reflect" @@ -595,6 +596,11 @@ func (t *TypeScriptify) convertType(depth int, typeOf reflect.Type, customCode m t.alreadyConverted[typeOf.String()] = true entityName := t.Prefix + typeOf.Name() + t.Suffix + + if typeClashWithReservedKeyword(entityName) { + warnAboutTypesClash(entityName) + } + result := "" if t.CreateInterface { result += fmt.Sprintf("interface %s {\n", entityName) @@ -901,3 +907,21 @@ func differentNamespaces(namespace string, typeOf reflect.Type) bool { } return false } + +func typeClashWithReservedKeyword(input string) bool { + in := strings.ToLower(strings.TrimSpace(input)) + for _, v := range jsReservedKeywords { + if in == v { + return true + } + } + + return false +} + +func warnAboutTypesClash(entity string) { + // TODO: Refactor logging + l := log.New(os.Stderr, "", 0) + l.Println(fmt.Sprintf("Usage of reserved keyword found and not supported: %s", entity)) + log.Println("Please rename returned type or consider adding bindings config to your wails.json") +} diff --git a/v2/pkg/commands/bindings/bindings.go b/v2/pkg/commands/bindings/bindings.go index 1bf0dae48..251d136ad 100644 --- a/v2/pkg/commands/bindings/bindings.go +++ b/v2/pkg/commands/bindings/bindings.go @@ -2,12 +2,15 @@ package bindings import ( "fmt" - "github.com/samber/lo" - "github.com/wailsapp/wails/v2/internal/shell" - "github.com/wailsapp/wails/v2/pkg/commands/buildtags" + "log" "os" "path/filepath" "runtime" + + "github.com/samber/lo" + "github.com/wailsapp/wails/v2/internal/colour" + "github.com/wailsapp/wails/v2/internal/shell" + "github.com/wailsapp/wails/v2/pkg/commands/buildtags" ) // Options for generating bindings @@ -16,6 +19,8 @@ type Options struct { Tags []string ProjectDirectory string GoModTidy bool + TsPrefix string + TsSuffix string } // GenerateBindings generates bindings for the Wails project in the given ProjectDirectory. @@ -57,10 +62,14 @@ func GenerateBindings(options Options) (string, error) { _ = os.Remove(filename) }() - stdout, stderr, err = shell.RunCommand(workingDirectory, filename) + stdout, stderr, err = shell.RunCommand(workingDirectory, filename, "-tsprefix", options.TsPrefix, "-tssuffix", options.TsSuffix) if err != nil { return stdout, fmt.Errorf("%s\n%s\n%s", stdout, stderr, err) } + if stderr != "" { + log.Println(colour.DarkYellow(stderr)) + } + return stdout, nil } diff --git a/v2/pkg/commands/build/build.go b/v2/pkg/commands/build/build.go index 28704f373..931538be3 100644 --- a/v2/pkg/commands/build/build.go +++ b/v2/pkg/commands/build/build.go @@ -199,6 +199,8 @@ func GenerateBindings(buildOptions *Options) error { output, err := bindings.GenerateBindings(bindings.Options{ Tags: buildOptions.UserTags, GoModTidy: !buildOptions.SkipModTidy, + TsPrefix: buildOptions.ProjectData.Bindings.TsGeneration.Prefix, + TsSuffix: buildOptions.ProjectData.Bindings.TsGeneration.Suffix, }) if err != nil { return err diff --git a/website/src/pages/changelog.mdx b/website/src/pages/changelog.mdx index 6ba07b58e..d3e490fb8 100644 --- a/website/src/pages/changelog.mdx +++ b/website/src/pages/changelog.mdx @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `OpenInspectorOnStartup` to debug options to allow opening the WebInspector during startup of the application in debug mode. Added by @stffabi in [PR](https://github.com/wailsapp/wails/pull/2080) - On macOS `wails doctor` now also shows the version of Xcode installed. Added by @stffabi in [PR](https://github.com/wailsapp/wails/pull/2089) - The [AssetServer](/docs/reference/options#assetserver) now supports handling range-requests if the [Assets](/docs/reference/options/#assets-1) `fs.FS` provides an `io.ReadSeeker`. Added by @stffabi in [PR](https://github.com/wailsapp/wails/pull/2091) +- Add new property for the `wails.json` config file - `bindings`. More information on the new property can be found in the updated [schema](static/schemas/config.v2.json). Properties `prefix` and `suffix` allow you to control the generated TypeScript entity name in the `model.ts` file. Added by @OlegGulevskyy in [PR](https://github.com/wailsapp/wails/pull/2101) - The `WindowSetAlwaysOnTop` method is now exposed in the JS runtime. Fixed by @gotid in [PR](https://github.com/wailsapp/wails/pull/2128) ### Fixed diff --git a/website/static/schemas/config.v2.json b/website/static/schemas/config.v2.json index 0f90bd15b..22a74d562 100644 --- a/website/static/schemas/config.v2.json +++ b/website/static/schemas/config.v2.json @@ -226,5 +226,25 @@ } } } - } -} \ No newline at end of file + }, + "bindings": { + "type": "object", + "description": "Bindings configurations", + "properties": { + "ts_generation": { + "type": "object", + "description": "model.ts file generation config", + "properties": { + "prefix": { + "type": "string", + "description": "All generated JavaScript entities will be prefixed with this value" + }, + "suffix": { + "type": "string", + "description": "All generated JavaScript entities will be suffixed with this value" + } + } + } + } + } +}