fixes and improvements in broker and render
This commit is contained in:
+64
-79
@@ -4,8 +4,8 @@ import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"html/template"
|
||||
"io/fs"
|
||||
"log/slog"
|
||||
"maps"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
@@ -37,7 +37,10 @@ func defaultHTMLRender() *Render {
|
||||
EnableCache: false,
|
||||
TemplatesPath: "templates",
|
||||
TemplateData: TemplateData{},
|
||||
Functions: template.FuncMap{},
|
||||
Functions: template.FuncMap{
|
||||
"default": defaultIfEmpty,
|
||||
"dict": Dict,
|
||||
},
|
||||
templateCache: templateCache{},
|
||||
}
|
||||
}
|
||||
@@ -53,117 +56,99 @@ func (re *Render) apply(opts ...RenderOptions) *Render {
|
||||
opt(re)
|
||||
}
|
||||
}
|
||||
|
||||
return re
|
||||
}
|
||||
|
||||
func defaultIfEmpty(fallback, value string) string {
|
||||
if strings.TrimSpace(value) == "" {
|
||||
func defaultIfEmpty(fallback string, value any) string {
|
||||
str, ok := value.(string)
|
||||
if !ok || strings.TrimSpace(str) == "" {
|
||||
return fallback
|
||||
}
|
||||
return value
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
func (re *Render) Template(w http.ResponseWriter, tmpl string, td *TemplateData) error {
|
||||
var tc templateCache
|
||||
var err error
|
||||
|
||||
re.Functions["default"] = defaultIfEmpty
|
||||
func cloneFuncMap(src template.FuncMap) template.FuncMap {
|
||||
c := make(template.FuncMap)
|
||||
maps.Copy(c, src)
|
||||
return c
|
||||
}
|
||||
|
||||
func (re *Render) Template(w http.ResponseWriter, layoutName, pageName string, td *TemplateData) error {
|
||||
if td == nil {
|
||||
td = &TemplateData{}
|
||||
}
|
||||
|
||||
tc, err = re.getTemplateCache()
|
||||
tmpl, err := re.loadTemplateWithLayout(layoutName, pageName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t, ok := tc[tmpl]
|
||||
if !ok {
|
||||
return errors.New("can't get template from cache")
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
if err = t.Execute(buf, td); err != nil {
|
||||
if err = tmpl.ExecuteTemplate(buf, strings.TrimSuffix(layoutName, ".gohtml"), td); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = buf.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
_, err = buf.WriteTo(w)
|
||||
return err
|
||||
}
|
||||
|
||||
func (re *Render) getTemplateCache() (templateCache, error) {
|
||||
if len(re.templateCache) == 0 {
|
||||
cachedTemplates, err := re.createTemplateCache()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
re.templateCache = cachedTemplates
|
||||
func (re *Render) RenderComponent(name string, td *TemplateData) (string, error) {
|
||||
if td == nil {
|
||||
td = &TemplateData{}
|
||||
}
|
||||
|
||||
path := filepath.Join(re.TemplatesPath, "components", name)
|
||||
|
||||
files := []string{path}
|
||||
matches, err := filepath.Glob(filepath.Join(re.TemplatesPath, "components", "*.gohtml"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
files = append(files, matches...)
|
||||
|
||||
funcs := cloneFuncMap(re.Functions)
|
||||
tmpl, err := template.New(name).Funcs(funcs).ParseFiles(files...)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
err = tmpl.ExecuteTemplate(&buf, strings.TrimSuffix(name, ".gohtml"), td)
|
||||
return buf.String(), err
|
||||
}
|
||||
|
||||
func (re *Render) loadTemplateWithLayout(layoutName, pageName string) (*template.Template, error) {
|
||||
cacheKey := layoutName + "::" + pageName
|
||||
|
||||
if re.EnableCache {
|
||||
return re.templateCache, nil
|
||||
if tmpl, ok := re.templateCache[cacheKey]; ok {
|
||||
return tmpl, nil
|
||||
}
|
||||
}
|
||||
return re.createTemplateCache()
|
||||
}
|
||||
|
||||
func (re *Render) findHTMLFiles() ([]string, error) {
|
||||
var files []string
|
||||
layoutPath := filepath.Join(re.TemplatesPath, "layouts", layoutName)
|
||||
pagePath := filepath.Join(re.TemplatesPath, "pages", pageName)
|
||||
|
||||
err := filepath.WalkDir(re.TemplatesPath, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !d.IsDir() && filepath.Ext(path) == ".gohtml" {
|
||||
files = append(files, path)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
files := []string{layoutPath, pagePath}
|
||||
componentFiles, err := filepath.Glob(filepath.Join(re.TemplatesPath, "components", "*.gohtml"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
files = append(files, componentFiles...)
|
||||
|
||||
funcs := cloneFuncMap(re.Functions)
|
||||
tmpl, err := template.New(layoutName).Funcs(funcs).ParseFiles(files...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func (re *Render) createTemplateCache() (templateCache, error) {
|
||||
cache := templateCache{}
|
||||
var baseTemplates []string
|
||||
var renderTemplates []string
|
||||
|
||||
templates, err := re.findHTMLFiles()
|
||||
if err != nil {
|
||||
return cache, err
|
||||
if re.EnableCache {
|
||||
re.templateCache[cacheKey] = tmpl
|
||||
slog.Debug("template cached", "key", cacheKey)
|
||||
}
|
||||
|
||||
slog.Debug("templates", "templates", templates)
|
||||
|
||||
for _, file := range templates {
|
||||
filePathBase := filepath.Base(file)
|
||||
if strings.Contains(filePathBase, "layout") || strings.Contains(filePathBase, "fragment") {
|
||||
baseTemplates = append(baseTemplates, file)
|
||||
}
|
||||
}
|
||||
|
||||
for _, file := range templates {
|
||||
filePathBase := filepath.Base(file)
|
||||
if strings.Contains(filePathBase, "page") || strings.Contains(filePathBase, "component") {
|
||||
renderTemplates = append(baseTemplates, file)
|
||||
ts, err := template.New(filePathBase).Funcs(re.Functions).ParseFiles(append(baseTemplates, renderTemplates...)...)
|
||||
if err != nil {
|
||||
return cache, err
|
||||
}
|
||||
cache[filePathBase] = ts
|
||||
}
|
||||
}
|
||||
|
||||
return cache, nil
|
||||
return tmpl, nil
|
||||
}
|
||||
|
||||
// Pages contains pagination info.
|
||||
|
||||
Reference in New Issue
Block a user