5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-04 23:23:48 +08:00
wails/v3/internal/generator/render/renderer.go
Fabio Massaioli 37673eb24d
[v3] Fix binding generator bugs and prepare for Go 1.24 (#4045)
* Rename predicates source file

* Overhaul and document type predicates

* Fix model collection logic for named types

* Fix map key type rendering

* Fix map creation code

* Fix rendering of structs that implement marshaler interfaces

* Fix type cycle detection to take type args into account

* Fix enum and typeparam field initialisation

* Improve unsupported type warnings

* Remove internal models file

* Deduplicate template code

* Accept generic aliases in static analyser

* Support new `encoding/json` flag `omitzero`

* Handle special cases when rendering generic aliases

* Update npm test dependencies

* Test class aliases and implicit private dependencies

* Test marshaler combinations

* Test map key types

* Remove bad map keys from unrelated tests

* Test service discovery through generic aliases

* Test generic aliases

* Test warning messages

* Disable go1.24 tests

* Update changelog

* Restore rendering of injected lines in index file

* Test directives

* Add wails:ignore directive

* Fix typo

* Move injections to the bottom of service files

* Handle errors from closing files

* Do not emit messages when services define only lifecycle methods

* Add internal directive for services and models

* Update changelog

* Fix error in service templates

* Test internal directive on services/models

* Fix error in index template

* Base testdata updates

* Testdata for class aliases and implicit private dependencies

* Testdata for marshaler combinations

* Testdata for map key types

* Testdata for bad map key fixes

* Add weakly typed enums aka alias constants

* Testdata for enum and typeparam field fixes

* Testdata for generic aliases

* Testdata for warning messages

* Testdata for directives

* Testdata for weakly typed enums

* Update binding example

* Update services example

* Remove go1.24 testdata

* Update cli doc

* Fix analyser tests

* Fix windows tests... hopefully

* go mod tidy on examples

* Update bindings guide

---------

Co-authored-by: Lea Anthony <lea.anthony@gmail.com>
2025-02-09 09:44:34 +11:00

142 lines
3.8 KiB
Go

package render
import (
"go/types"
"io"
"slices"
"strings"
"text/template"
"github.com/wailsapp/wails/v3/internal/flags"
"github.com/wailsapp/wails/v3/internal/generator/collect"
)
// Renderer holds the template set for a given configuration.
// It provides methods for rendering various output modules.
type Renderer struct {
options *flags.GenerateBindingsOptions
collector *collect.Collector
ext string
service *template.Template
typedefs *template.Template
}
// NewRenderer initialises a code renderer
// for the given configuration and data collector.
func NewRenderer(options *flags.GenerateBindingsOptions, collector *collect.Collector) *Renderer {
ext := ".js"
if options.TS {
ext = ".ts"
}
return &Renderer{
options: options,
collector: collector,
ext: ext,
service: tmplService[tmplLanguage(options.TS)],
typedefs: tmplModels[tmplLanguage(options.TS)],
}
}
// ServiceFile returns the standard name of a service file
// for the given struct name, with the appropriate extension.
func (renderer *Renderer) ServiceFile(name string) string {
return strings.ToLower(name) + renderer.ext
}
// ModelsFile returns the standard name of a models file
// with the appropriate extension.
func (renderer *Renderer) ModelsFile() string {
return renderer.options.ModelsFilename + renderer.ext
}
// IndexFile returns the standard name of a package index file
// with the appropriate extension.
func (renderer *Renderer) IndexFile() string {
return renderer.options.IndexFilename + renderer.ext
}
// Service renders binding code for the given service type to w.
func (renderer *Renderer) Service(w io.Writer, info *collect.ServiceInfo) error {
return renderer.service.Execute(w, &struct {
module
Service *collect.ServiceInfo
}{
module{
Renderer: renderer,
GenerateBindingsOptions: renderer.options,
Imports: info.Imports,
},
info,
})
}
// Typedefs renders type definitions for the given list of models.
func (renderer *Renderer) Models(w io.Writer, imports *collect.ImportMap, models []*collect.ModelInfo) error {
if !renderer.options.UseInterfaces {
// Sort class aliases after the class they alias.
// Works in amortized linear time thanks to an auxiliary map.
// Track postponed class aliases and their dependencies.
aliases := make(map[types.Object][]*collect.ModelInfo, len(models))
models = slices.Clone(models)
for i, j := 0, 0; i < len(models); i++ {
if models[i].Type != nil && models[i].Predicates.IsClass {
// models[i] is a class alias:
// models[i].Type is guaranteed to be
// either an alias or a named type
obj := models[i].Type.(interface{ Obj() *types.TypeName }).Obj()
if obj.Pkg().Path() == imports.Self {
// models[i] aliases a type from the current module.
if a, ok := aliases[obj]; !ok || len(a) > 0 {
// The aliased type has not been visited already, postpone.
aliases[obj] = append(a, models[i])
continue
}
}
}
// Append models[i].
models[j] = models[i]
j++
// Keep appending aliases whose aliased type has been just appended.
for k := j - 1; k < j; k++ {
a := aliases[models[k].Object()]
aliases[models[k].Object()] = nil // Mark aliased model as visited
j += copy(models[j:], a)
}
}
}
return renderer.typedefs.Execute(w, &struct {
module
Models []*collect.ModelInfo
}{
module{
Renderer: renderer,
GenerateBindingsOptions: renderer.options,
Imports: imports,
},
models,
})
}
// Index renders the given package index to w.
func (renderer *Renderer) Index(w io.Writer, index *collect.PackageIndex) error {
return tmplIndex.Execute(w, &struct {
*collect.PackageIndex
*Renderer
*flags.GenerateBindingsOptions
}{
index,
renderer,
renderer.options,
})
}