Files
fn_registry/functions/infra/scaffold_wails_app.go
T
egutierrez e02a950ee0 feat: funciones Wails — scaffold, CRUD bindings, build, eventos y streaming
Funciones Go para crear apps Wails: scaffold estructura, bind CRUD genérico,
build multiplataforma, emit eventos y stream de datos al frontend.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 20:55:24 +02:00

216 lines
5.2 KiB
Go

package infra
import (
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"text/template"
)
// ScaffoldWailsAppConfig configura la generación del proyecto Wails.
type ScaffoldWailsAppConfig struct {
Name string // Nombre del proyecto
Dir string // Directorio destino
Title string // Título de la ventana
Width int // Ancho de la ventana (default 1024)
Height int // Alto de la ventana (default 768)
Author string // Nombre del autor
FrontendLib string // Path a la frontend library (default ~/.local_agentes/frontend/frontend)
}
const mainGoTpl = `package main
import (
"embed"
"github.com/wailsapp/wails/v2"
"github.com/wailsapp/wails/v2/pkg/options"
"github.com/wailsapp/wails/v2/pkg/options/assetserver"
)
//go:embed all:frontend/dist
var assets embed.FS
func main() {
app := NewApp()
err := wails.Run(&options.App{
Title: "{{.Title}}",
Width: {{.Width}},
Height: {{.Height}},
AssetServer: &assetserver.Options{
Assets: assets,
},
BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1},
OnStartup: app.startup,
Bind: []interface{}{
app,
},
})
if err != nil {
println("Error:", err.Error())
}
}
`
const appGoTpl = `package main
import (
"context"
"github.com/wailsapp/wails/v2/pkg/runtime"
)
// App struct — cada método público se expone como binding IPC al frontend.
type App struct {
ctx context.Context
}
func NewApp() *App {
return &App{}
}
func (a *App) startup(ctx context.Context) {
a.ctx = ctx
}
// EmitEvent emite un evento al frontend.
func (a *App) EmitEvent(eventName string, data interface{}) {
runtime.EventsEmit(a.ctx, eventName, data)
}
// Ping verifica que el IPC funciona.
func (a *App) Ping() string {
return "pong"
}
`
const wailsJSONTpl = `{
"$schema": "https://wails.io/schemas/config.v2.json",
"name": "{{.Name}}",
"outputfilename": "{{.Name}}",
"frontend:dir": "./frontend",
"frontend:install": "pnpm install",
"frontend:build": "pnpm run build",
"frontend:dev:watcher": "pnpm run dev",
"frontend:dev:serverUrl": "auto",
"wailsjsdir": "./frontend/src/wailsjs",
"author": {
"name": "{{.Author}}"
}
}
`
const goModTpl = `module {{.Name}}
go 1.23
require github.com/wailsapp/wails/v2 v2.11.0
`
// ScaffoldWailsApp genera la estructura base de un proyecto Wails con frontend vinculado.
func ScaffoldWailsApp(ctx context.Context, cfg ScaffoldWailsAppConfig) error {
if cfg.Name == "" {
return fmt.Errorf("name is required")
}
if cfg.Dir == "" {
cfg.Dir = cfg.Name
}
if cfg.Title == "" {
cfg.Title = cfg.Name
}
if cfg.Width == 0 {
cfg.Width = 1024
}
if cfg.Height == 0 {
cfg.Height = 768
}
if cfg.Author == "" {
cfg.Author = "Agent"
}
if cfg.FrontendLib == "" {
home, _ := os.UserHomeDir()
cfg.FrontendLib = filepath.Join(home, ".local_agentes", "frontend", "frontend")
}
// Crear directorio
if err := os.MkdirAll(cfg.Dir, 0755); err != nil {
return fmt.Errorf("creating dir: %w", err)
}
// Generar archivos Go
files := map[string]string{
"main.go": mainGoTpl,
"app.go": appGoTpl,
"wails.json": wailsJSONTpl,
"go.mod": goModTpl,
}
for name, tpl := range files {
path := filepath.Join(cfg.Dir, name)
t, err := template.New(name).Parse(tpl)
if err != nil {
return fmt.Errorf("parsing template %s: %w", name, err)
}
f, err := os.Create(path)
if err != nil {
return fmt.Errorf("creating %s: %w", name, err)
}
if err := t.Execute(f, cfg); err != nil {
f.Close()
return fmt.Errorf("executing template %s: %w", name, err)
}
f.Close()
}
// Crear frontend como link simbólico o copiar template
frontendDir := filepath.Join(cfg.Dir, "frontend")
createProjectScript := filepath.Join(filepath.Dir(cfg.FrontendLib), "..", "scripts", "create-project.sh")
if _, err := os.Stat(createProjectScript); err == nil {
// Usar el script de Frontend_Library
cmd := exec.CommandContext(ctx, "bash", createProjectScript, cfg.Name, frontendDir, "--wails")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
return fmt.Errorf("creating frontend: %w", err)
}
} else {
// Fallback: crear frontend mínimo
if err := os.MkdirAll(filepath.Join(frontendDir, "src"), 0755); err != nil {
return fmt.Errorf("creating frontend dir: %w", err)
}
pkgJSON := fmt.Sprintf(`{"name":"%s-frontend","private":true,"scripts":{"dev":"vite","build":"vite build"}}`, cfg.Name)
os.WriteFile(filepath.Join(frontendDir, "package.json"), []byte(pkgJSON), 0644)
}
// go mod tidy
cmd := exec.CommandContext(ctx, "go", "mod", "tidy")
cmd.Dir = cfg.Dir
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
_ = cmd.Run() // No fatal si falla
fmt.Printf("Wails app scaffolded at %s\n", cfg.Dir)
return nil
}
// GenerateAppBinding genera el código Go para un método de binding.
func GenerateAppBinding(name string, params []string, returnType string, body string) string {
var sb strings.Builder
sb.WriteString(fmt.Sprintf("func (a *App) %s(", name))
sb.WriteString(strings.Join(params, ", "))
sb.WriteString(")")
if returnType != "" {
sb.WriteString(" " + returnType)
}
sb.WriteString(" {\n")
sb.WriteString("\t" + body + "\n")
sb.WriteString("}\n")
return sb.String()
}